Merge "Trace each config option in RenderEngine's shader priming" into main
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index a5d176d..fdb032b 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -117,6 +117,7 @@
"libdumpsys",
"libserviceutils",
"android.tracing.flags_c_lib",
+ "perfetto_flags_c_lib",
],
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 888fb67..9e3e2b0 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -57,6 +57,7 @@
#include <log/log_read.h>
#include <math.h>
#include <openssl/sha.h>
+#include <perfetto_flags.h>
#include <poll.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -190,7 +191,7 @@
#define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log"
#define LINKERCONFIG_DIR "/linkerconfig"
#define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
-#define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace"
+#define SYSTEM_TRACE_DIR "/data/misc/perfetto-traces/bugreport"
#define CGROUPFS_DIR "/sys/fs/cgroup"
#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
#define DROPBOX_DIR "/data/system/dropbox"
@@ -359,6 +360,31 @@
return CopyFileToFd(input_file, out_fd.get());
}
+template <typename Func>
+size_t ForEachTrace(Func func) {
+ std::unique_ptr<DIR, decltype(&closedir)> traces_dir(opendir(SYSTEM_TRACE_DIR), closedir);
+
+ if (traces_dir == nullptr) {
+ MYLOGW("Unable to open directory %s: %s\n", SYSTEM_TRACE_DIR, strerror(errno));
+ return 0;
+ }
+
+ size_t traces_found = 0;
+ struct dirent* entry = nullptr;
+ while ((entry = readdir(traces_dir.get()))) {
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+ std::string trace_path = std::string(SYSTEM_TRACE_DIR) + "/" + entry->d_name;
+ if (access(trace_path.c_str(), F_OK) != 0) {
+ continue;
+ }
+ ++traces_found;
+ func(trace_path);
+ }
+ return traces_found;
+}
+
} // namespace
} // namespace os
} // namespace android
@@ -1101,20 +1127,16 @@
// This function copies into the .zip the system trace that was snapshotted
// by the early call to MaybeSnapshotSystemTraceAsync(), if any background
// tracing was happening.
- bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
- if (!system_trace_exists) {
- // No background trace was happening at the time MaybeSnapshotSystemTraceAsync() was invoked
- if (!PropertiesHelper::IsUserBuild()) {
- MYLOGI(
- "No system traces found. Check for previously uploaded traces by looking for "
- "go/trace-uuid in logcat")
- }
- return;
+ size_t traces_found = android::os::ForEachTrace([&](const std::string& trace_path) {
+ ds.AddZipEntry(ZIP_ROOT_DIR + trace_path, trace_path);
+ android::os::UnlinkAndLogOnError(trace_path);
+ });
+
+ if (traces_found == 0 && !PropertiesHelper::IsUserBuild()) {
+ MYLOGI(
+ "No system traces found. Check for previously uploaded traces by looking for "
+ "go/trace-uuid in logcat")
}
- ds.AddZipEntry(
- ZIP_ROOT_DIR + SYSTEM_TRACE_SNAPSHOT,
- SYSTEM_TRACE_SNAPSHOT);
- android::os::UnlinkAndLogOnError(SYSTEM_TRACE_SNAPSHOT);
}
static void DumpVisibleWindowViews() {
@@ -3412,8 +3434,8 @@
// duration is logged into MYLOG instead.
PrintHeader();
- bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
- if (options_->use_predumped_ui_data && !system_trace_exists) {
+ size_t trace_count = android::os::ForEachTrace([](const std::string&) {});
+ if (options_->use_predumped_ui_data && trace_count == 0) {
MYLOGW("Ignoring 'use predumped data' flag because no predumped data is available");
options_->use_predumped_ui_data = false;
}
@@ -3560,20 +3582,24 @@
}
// If a stale file exists already, remove it.
- unlink(SYSTEM_TRACE_SNAPSHOT);
+ android::os::ForEachTrace([&](const std::string& trace_path) { unlink(trace_path.c_str()); });
MYLOGI("Launching async '%s'", SERIALIZE_PERFETTO_TRACE_TASK.c_str())
+
return std::async(
std::launch::async, [this, outPath = std::move(outPath), outFd = std::move(outFd)] {
- // If a background system trace is happening and is marked as "suitable for
- // bugreport" (i.e. bugreport_score > 0 in the trace config), this command
- // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely)
- // case that no trace is ongoing, this command is a no-op.
+ // If one or more background system traces are happening and are marked as
+ // "suitable for bugreport" (bugreport_score > 0 in the trace config), this command
+ // will snapshot them into SYSTEM_TRACE_DIR.
+ // In the (likely) case that no trace is ongoing, this command is a no-op.
// Note: this should not be enqueued as we need to freeze the trace before
// dumpstate starts. Otherwise the trace ring buffers will contain mostly
// the dumpstate's own activity which is irrelevant.
+ const char* cmd_arg = perfetto::flags::save_all_traces_in_bugreport()
+ ? "--save-all-for-bugreport"
+ : "--save-for-bugreport";
RunCommand(
- SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"},
+ SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", cmd_arg},
CommandOptions::WithTimeout(30).DropRoot().CloseAllFileDescriptorsOnExec().Build(),
false, outFd);
// MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
diff --git a/cmds/dumpstate/dumpstate_smoke_test.xml b/cmds/dumpstate/dumpstate_smoke_test.xml
index 0aff200..7e3307d 100644
--- a/cmds/dumpstate/dumpstate_smoke_test.xml
+++ b/cmds/dumpstate/dumpstate_smoke_test.xml
@@ -22,7 +22,9 @@
<option name="cleanup" value="true" />
<option name="push" value="dumpstate_smoke_test->/data/local/tmp/dumpstate_smoke_test" />
</target_preparer>
-
+ <target_preparer class="com.android.tradefed.targetprep.FeatureFlagTargetPreparer">
+ <option name="flag-value" value="perfetto/perfetto.flags.save_all_traces_in_bugreport=true" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="dumpstate_smoke_test" />
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index a29923a..c72847c 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -24,8 +24,10 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libgen.h>
+#include <signal.h>
#include <ziparchive/zip_archive.h>
+#include <cstdio>
#include <fstream>
#include <regex>
@@ -603,6 +605,93 @@
listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
}
+class DumpstateTracingTest : public Test {
+ protected:
+ void TearDown() override {
+ for (int pid : bg_process_pids) {
+ kill(pid, SIGKILL);
+ }
+ }
+
+ void StartTracing(const std::string& config) {
+ // Write the perfetto config into a file.
+ const int id = static_cast<int>(bg_process_pids.size());
+ char cfg[64];
+ snprintf(cfg, sizeof(cfg), "/data/misc/perfetto-configs/br-%d", id);
+ unlink(cfg); // Remove the config file if it exists already.
+ FILE* f = fopen(cfg, "w");
+ ASSERT_NE(f, nullptr);
+ fputs(config.c_str(), f);
+ fclose(f);
+
+ // Invoke perfetto to start tracing.
+ char cmd[255];
+ snprintf(cmd, sizeof(cmd), "perfetto --background-wait --txt -o /dev/null -c %s", cfg);
+ FILE* proc = popen(cmd, "r");
+ ASSERT_NE(proc, nullptr);
+
+ // Read back the PID of the background process. We will use it to kill
+ // all tracing sessions when the test ends or fails.
+ char pid_str[32]{};
+ ASSERT_NE(fgets(pid_str, sizeof(pid_str), proc), nullptr);
+ int pid = atoi(pid_str);
+ bg_process_pids.push_back(pid);
+
+ pclose(proc);
+ unlink(cfg);
+ }
+
+ std::vector<int> bg_process_pids;
+};
+
+TEST_F(DumpstateTracingTest, ManyTracesInBugreport) {
+ // Note the trace duration is irrelevant and is only an upper bound.
+ // Tracing is stopped as soon as the bugreport.zip creation ends.
+ StartTracing(R"(
+buffers { size_kb: 4096 }
+data_sources {
+ config {
+ name: "linux.ftrace"
+ }
+}
+
+duration_ms: 120000
+bugreport_filename: "sys.pftrace"
+bugreport_score: 100
+)");
+
+ StartTracing(R"(
+buffers { size_kb: 4096 }
+data_sources {
+ config {
+ name: "linux.ftrace"
+ }
+}
+
+duration_ms: 120000
+bugreport_score: 50
+bugreport_filename: "mem.pftrace"
+)");
+
+ ZippedBugreportGenerationTest::GenerateBugreport();
+ std::string zip_path = ZippedBugreportGenerationTest::getZipFilePath();
+ ZipArchiveHandle handle;
+ ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);
+
+ const char* kExpectedEntries[]{
+ "FS/data/misc/perfetto-traces/bugreport/sys.pftrace",
+ "FS/data/misc/perfetto-traces/bugreport/mem.pftrace",
+ };
+
+ // Check that the bugreport contains both traces.
+ for (const char* file_path : kExpectedEntries) {
+ ZipEntry entry{};
+ GetEntry(handle, file_path, &entry);
+ EXPECT_GT(entry.uncompressed_length, 100);
+ }
+ CloseArchive(handle);
+}
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/include/android/input.h b/include/android/input.h
index 5f44550..2f6c5b5 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -862,7 +862,7 @@
AMOTION_EVENT_BUTTON_FORWARD = 1 << 4,
AMOTION_EVENT_BUTTON_STYLUS_PRIMARY = 1 << 5,
AMOTION_EVENT_BUTTON_STYLUS_SECONDARY = 1 << 6,
- // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs)
+ // LINT.ThenChange(/frameworks/native/libs/input/rust/input.rs,/frameworks/native/services/inputflinger/tests/fuzzers/FuzzedInputStream.h)
};
/**
diff --git a/include/input/Input.h b/include/input/Input.h
index e84023e..002b3a7 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -316,6 +316,19 @@
bool isStylusEvent(uint32_t source, const std::vector<PointerProperties>& properties);
+bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties,
+ int32_t action);
+
+bool isFromMouse(uint32_t source, ToolType tooltype);
+
+bool isFromTouchpad(uint32_t source, ToolType tooltype);
+
+bool isFromDrawingTablet(uint32_t source, ToolType tooltype);
+
+bool isHoverAction(int32_t action);
+
+bool isMouseOrTouchpad(uint32_t sources);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
diff --git a/include/input/InputFlags.h b/include/input/InputFlags.h
new file mode 100644
index 0000000..0e194ea
--- /dev/null
+++ b/include/input/InputFlags.h
@@ -0,0 +1,30 @@
+/*
+ * 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 {
+
+class InputFlags {
+public:
+ /**
+ * Check if connected displays feature is enabled, either via the feature flag or settings
+ * override.
+ */
+ static bool connectedDisplaysCursorEnabled();
+};
+
+} // namespace android
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index bcbd14f..e848385 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -79,7 +79,7 @@
template <typename T>
typename std::enable_if<std::is_base_of<Flattenable<T>, T>::value, status_t>::type read(
const Parcel& parcel, sp<T>* t) const {
- *t = new T{};
+ *t = sp<T>::make();
return callParcel("read(sp<Flattenable>)", [&]() { return parcel.read(*(t->get())); });
}
template <typename T>
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 089c775..8050205 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -27,6 +27,10 @@
#if defined(__ANDROID_VENDOR__)
+#if defined(__ANDROID_PRODUCT__)
+#error "build bug: product is not part of the vendor half of the Treble system/vendor split"
+#endif
+
/**
* Private addition to binder_flag_t.
*/
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 8c0501b..6a8a698 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1201,5 +1201,45 @@
Ok(v.map(|v| v.into_iter().map(Self).collect()))
}
}
+
+ impl std::ops::BitOr for $enum {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self {
+ Self(self.0 | rhs.0)
+ }
+ }
+
+ impl std::ops::BitOrAssign for $enum {
+ fn bitor_assign(&mut self, rhs: Self) {
+ self.0 = self.0 | rhs.0;
+ }
+ }
+
+ impl std::ops::BitAnd for $enum {
+ type Output = Self;
+ fn bitand(self, rhs: Self) -> Self {
+ Self(self.0 & rhs.0)
+ }
+ }
+
+ impl std::ops::BitAndAssign for $enum {
+ fn bitand_assign(&mut self, rhs: Self) {
+ self.0 = self.0 & rhs.0;
+ }
+ }
+
+ impl std::ops::BitXor for $enum {
+ type Output = Self;
+ fn bitxor(self, rhs: Self) -> Self {
+ Self(self.0 ^ rhs.0)
+ }
+ }
+
+ impl std::ops::BitXorAssign for $enum {
+ fn bitxor_assign(&mut self, rhs: Self) {
+ self.0 = self.0 ^ rhs.0;
+ }
+ }
+
};
}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index c0cac83..609334e 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -28,8 +28,9 @@
/// `num_threads` additional threads as specified by
/// [`set_thread_pool_max_thread_count`](Self::set_thread_pool_max_thread_count).
///
- /// This should be done before creating any Binder client or server. If
- /// neither this nor [`join_thread_pool`](Self::join_thread_pool) are
+ /// If this is called, it must be done before creating any Binder client or server.
+ ///
+ /// If neither this nor [`join_thread_pool`](Self::join_thread_pool) are
/// called, then some things (such as callbacks and
/// [`IBinder::link_to_death`](crate::IBinder::link_to_death)) will silently
/// not work: the callbacks will be queued but never called as there is no
diff --git a/libs/graphicsenv/FeatureOverrides.cpp b/libs/graphicsenv/FeatureOverrides.cpp
index 6974da9..51afe28 100644
--- a/libs/graphicsenv/FeatureOverrides.cpp
+++ b/libs/graphicsenv/FeatureOverrides.cpp
@@ -14,14 +14,46 @@
* limitations under the License.
*/
-#include <graphicsenv/FeatureOverrides.h>
+#include <cinttypes>
#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <graphicsenv/FeatureOverrides.h>
namespace android {
using base::StringAppendF;
+status_t FeatureConfig::writeToParcel(Parcel* parcel) const {
+ status_t status;
+
+ status = parcel->writeUtf8AsUtf16(mFeatureName);
+ if (status != OK) {
+ return status;
+ }
+ status = parcel->writeBool(mEnabled);
+ if (status != OK) {
+ return status;
+ }
+
+ return OK;
+}
+
+status_t FeatureConfig::readFromParcel(const Parcel* parcel) {
+ status_t status;
+
+ status = parcel->readUtf8FromUtf16(&mFeatureName);
+ if (status != OK) {
+ return status;
+ }
+ status = parcel->readBool(&mEnabled);
+ if (status != OK) {
+ return status;
+ }
+
+ return OK;
+}
+
std::string FeatureConfig::toString() const {
std::string result;
StringAppendF(&result, "Feature: %s\n", mFeatureName.c_str());
@@ -30,6 +62,91 @@
return result;
}
+status_t FeatureOverrides::writeToParcel(Parcel* parcel) const {
+ status_t status;
+ // Number of global feature configs.
+ status = parcel->writeVectorSize(mGlobalFeatures);
+ if (status != OK) {
+ return status;
+ }
+ // Global feature configs.
+ for (const auto& cfg : mGlobalFeatures) {
+ status = cfg.writeToParcel(parcel);
+ if (status != OK) {
+ return status;
+ }
+ }
+ // Number of package feature overrides.
+ status = parcel->writeInt32(static_cast<int32_t>(mPackageFeatures.size()));
+ if (status != OK) {
+ return status;
+ }
+ for (const auto& feature : mPackageFeatures) {
+ // Package name.
+ status = parcel->writeUtf8AsUtf16(feature.first);
+ if (status != OK) {
+ return status;
+ }
+ // Number of package feature configs.
+ status = parcel->writeVectorSize(feature.second);
+ if (status != OK) {
+ return status;
+ }
+ // Package feature configs.
+ for (const auto& cfg : feature.second) {
+ status = cfg.writeToParcel(parcel);
+ if (status != OK) {
+ return status;
+ }
+ }
+ }
+
+ return OK;
+}
+
+status_t FeatureOverrides::readFromParcel(const Parcel* parcel) {
+ status_t status;
+
+ // Number of global feature configs.
+ status = parcel->resizeOutVector(&mGlobalFeatures);
+ if (status != OK) {
+ return status;
+ }
+ // Global feature configs.
+ for (FeatureConfig& cfg : mGlobalFeatures) {
+ status = cfg.readFromParcel(parcel);
+ if (status != OK) {
+ return status;
+ }
+ }
+
+ // Number of package feature overrides.
+ int numPkgOverrides = parcel->readInt32();
+ for (int i = 0; i < numPkgOverrides; i++) {
+ // Package name.
+ std::string name;
+ status = parcel->readUtf8FromUtf16(&name);
+ if (status != OK) {
+ return status;
+ }
+ std::vector<FeatureConfig> cfgs;
+ // Number of package feature configs.
+ int numCfgs = parcel->readInt32();
+ // Package feature configs.
+ for (int j = 0; j < numCfgs; j++) {
+ FeatureConfig cfg;
+ status = cfg.readFromParcel(parcel);
+ if (status != OK) {
+ return status;
+ }
+ cfgs.emplace_back(cfg);
+ }
+ mPackageFeatures[name] = cfgs;
+ }
+
+ return OK;
+}
+
std::string FeatureOverrides::toString() const {
std::string result;
result.append("Global Features:\n");
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 42e7c37..9a34aff 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -119,6 +119,21 @@
}
return driverPath;
}
+
+ FeatureOverrides getFeatureOverrides() override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ FeatureOverrides featureOverrides;
+ status_t error =
+ remote()->transact(BnGpuService::GET_FEATURE_CONFIG_OVERRIDES, data, &reply);
+ if (error != OK) {
+ return featureOverrides;
+ }
+
+ featureOverrides.readFromParcel(&reply);
+ return featureOverrides;
+ }
};
IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService");
@@ -271,6 +286,15 @@
toggleAngleAsSystemDriver(enableAngleAsSystemDriver);
return OK;
}
+ case GET_FEATURE_CONFIG_OVERRIDES: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ // Get the FeatureOverrides from gpuservice, which implements the IGpuService interface
+ // with GpuService::getFeatureOverrides().
+ FeatureOverrides featureOverrides = getFeatureOverrides();
+ featureOverrides.writeToParcel(reply);
+ return OK;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
index 2b94187..450eed2 100644
--- a/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
+++ b/libs/graphicsenv/include/graphicsenv/FeatureOverrides.h
@@ -20,13 +20,17 @@
#include <string>
#include <vector>
+#include <binder/Parcelable.h>
+
namespace android {
-class FeatureConfig {
+class FeatureConfig : public Parcelable {
public:
FeatureConfig() = default;
FeatureConfig(const FeatureConfig&) = default;
virtual ~FeatureConfig() = default;
+ virtual status_t writeToParcel(Parcel* parcel) const;
+ virtual status_t readFromParcel(const Parcel* parcel);
std::string toString() const;
std::string mFeatureName;
@@ -37,11 +41,13 @@
* Class for transporting OpenGL ES Feature configurations from GpuService to authorized
* recipients.
*/
-class FeatureOverrides {
+class FeatureOverrides : public Parcelable {
public:
FeatureOverrides() = default;
FeatureOverrides(const FeatureOverrides&) = default;
virtual ~FeatureOverrides() = default;
+ virtual status_t writeToParcel(Parcel* parcel) const;
+ virtual status_t readFromParcel(const Parcel* parcel);
std::string toString() const;
std::vector<FeatureConfig> mGlobalFeatures;
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index a0d6e37..442683a 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -18,6 +18,7 @@
#include <binder/IInterface.h>
#include <cutils/compiler.h>
+#include <graphicsenv/FeatureOverrides.h>
#include <graphicsenv/GpuStatsInfo.h>
#include <vector>
@@ -55,6 +56,9 @@
// sets ANGLE as system GLES driver if enabled==true by setting persist.graphics.egl to true.
virtual void toggleAngleAsSystemDriver(bool enabled) = 0;
+
+ // Get the list of features to override.
+ virtual FeatureOverrides getFeatureOverrides() = 0;
};
class BnGpuService : public BnInterface<IGpuService> {
@@ -67,6 +71,7 @@
TOGGLE_ANGLE_AS_SYSTEM_DRIVER,
SET_TARGET_STATS_ARRAY,
ADD_VULKAN_ENGINE_NAME,
+ GET_FEATURE_CONFIG_OVERRIDES,
// Always append new enum to the end.
};
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index c770db9..310f781 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -222,8 +222,6 @@
ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
- mNumAcquired = 0;
- mNumFrameAvailable = 0;
TransactionCompletedListener::getInstance()->addQueueStallListener(
[&](const std::string& reason) {
@@ -439,7 +437,7 @@
void BLASTBufferQueue::flushShadowQueue() {
BQA_LOGV("flushShadowQueue");
- int numFramesToFlush = mNumFrameAvailable;
+ int32_t numFramesToFlush = mNumFrameAvailable;
while (numFramesToFlush > 0) {
acquireNextBufferLocked(std::nullopt);
numFramesToFlush--;
diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index ab747b9..fb3e0f1 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -69,7 +69,7 @@
Choreographer::Context Choreographer::gChoreographers;
-static thread_local Choreographer* gChoreographer;
+static thread_local sp<Choreographer> gChoreographer;
void Choreographer::initJVM(JNIEnv* env) {
env->GetJavaVM(&gJni.jvm);
@@ -86,21 +86,21 @@
"()V");
}
-Choreographer* Choreographer::getForThread() {
+sp<Choreographer> Choreographer::getForThread() {
if (gChoreographer == nullptr) {
sp<Looper> looper = Looper::getForThread();
if (!looper.get()) {
ALOGW("No looper prepared for thread");
return nullptr;
}
- gChoreographer = new Choreographer(looper);
+ gChoreographer = sp<Choreographer>::make(looper);
status_t result = gChoreographer->initialize();
if (result != OK) {
ALOGW("Failed to initialize");
return nullptr;
}
}
- return gChoreographer;
+ return gChoreographer.get();
}
Choreographer::Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle)
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 3ad0e52..67de742 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -385,6 +385,26 @@
}
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+status_t ConsumerBase::addReleaseFence(const sp<GraphicBuffer> buffer, const sp<Fence>& fence) {
+ CB_LOGV("addReleaseFence");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ CB_LOGE("addReleaseFence: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ int slotIndex = getSlotForBufferLocked(buffer);
+ if (slotIndex == BufferQueue::INVALID_BUFFER_SLOT) {
+ return BAD_VALUE;
+ }
+
+ return addReleaseFenceLocked(slotIndex, buffer, fence);
+}
+
status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) {
Mutex::Autolock _l(mMutex);
if (mAbandoned) {
diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index a93ba14..5862967 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -103,7 +103,7 @@
virtual void handleMessage(const Message& message) override;
static void initJVM(JNIEnv* env);
- static Choreographer* getForThread();
+ static sp<Choreographer> getForThread();
static void signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock);
static int64_t getStartTimeNanosForVsyncId(AVsyncId vsyncId) EXCLUDES(gChoreographers.lock);
virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index acb0006..2e347c9 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -98,6 +98,8 @@
status_t detachBuffer(const sp<GraphicBuffer>& buffer);
#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ status_t addReleaseFence(const sp<GraphicBuffer> buffer, const sp<Fence>& fence);
+
// See IGraphicBufferConsumer::setDefaultBufferSize
status_t setDefaultBufferSize(uint32_t width, uint32_t height);
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index d04b861..c2680a4 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -502,12 +502,18 @@
Rect layerStackSpaceRect = Rect::EMPTY_RECT;
Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
- // Exclusive to virtual displays: The sink surface into which the virtual display is rendered,
- // and an optional resolution that overrides its default dimensions.
- sp<IGraphicBufferProducer> surface;
+ // For physical displays, this is the resolution, which must match the active display mode. To
+ // change the resolution, the client must first call SurfaceControl.setDesiredDisplayModeSpecs
+ // with the new DesiredDisplayModeSpecs#defaultMode, then commit the matching width and height.
+ //
+ // For virtual displays, this is an optional resolution that overrides its default dimensions.
+ //
uint32_t width = 0;
uint32_t height = 0;
+ // For virtual displays, this is the sink surface into which the virtual display is rendered.
+ sp<IGraphicBufferProducer> surface;
+
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
};
diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp
index 8db48d2..314dea6 100644
--- a/libs/gui/tests/Choreographer_test.cpp
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -50,7 +50,7 @@
TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) {
sp<Looper> looper = Looper::prepare(0);
- Choreographer* choreographer = Choreographer::getForThread();
+ sp<Choreographer> choreographer = Choreographer::getForThread();
VsyncCallback animationCb;
choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0,
CALLBACK_ANIMATION);
@@ -83,4 +83,4 @@
animationCb.frameTime.count());
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index d2e4320..ff26184 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -230,6 +230,7 @@
"InputConsumerNoResampling.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
+ "InputFlags.cpp",
"InputTransport.cpp",
"InputVerifier.cpp",
"KeyCharacterMap.cpp",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 65a088e..155ea00 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -284,6 +284,36 @@
return false;
}
+bool isStylusHoverEvent(uint32_t source, const std::vector<PointerProperties>& properties,
+ int32_t action) {
+ return isStylusEvent(source, properties) && isHoverAction(action);
+}
+
+bool isFromMouse(uint32_t source, ToolType toolType) {
+ return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::MOUSE;
+}
+
+bool isFromTouchpad(uint32_t source, ToolType toolType) {
+ return isFromSource(source, AINPUT_SOURCE_MOUSE) && toolType == ToolType::FINGER;
+}
+
+bool isFromDrawingTablet(uint32_t source, ToolType toolType) {
+ return isFromSource(source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
+ isStylusToolType(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 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));
+}
+
VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
event.getSource(), event.getDisplayId()},
diff --git a/libs/input/InputFlags.cpp b/libs/input/InputFlags.cpp
new file mode 100644
index 0000000..555b138
--- /dev/null
+++ b/libs/input/InputFlags.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 <input/InputFlags.h>
+
+#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#include <cutils/properties.h>
+
+#include <string>
+
+namespace android {
+
+bool InputFlags::connectedDisplaysCursorEnabled() {
+ static std::optional<bool> cachedDevOption;
+ if (!cachedDevOption.has_value()) {
+ char value[PROPERTY_VALUE_MAX];
+ constexpr static auto sysprop_name = "persist.wm.debug.desktop_experience_devopts";
+ const int devOptionEnabled =
+ property_get(sysprop_name, value, nullptr) > 0 ? std::atoi(value) : 0;
+ cachedDevOption = devOptionEnabled == 1;
+ }
+ if (cachedDevOption.value_or(false)) {
+ return true;
+ }
+ return com::android::input::flags::connected_displays_cursor();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 5bb30db..4aafb0e 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -61,13 +61,6 @@
}
flag {
- name: "report_palms_to_gestures_library"
- namespace: "input"
- description: "Report touches marked as palm by firmware to gestures library"
- bug: "302505955"
-}
-
-flag {
name: "enable_touchpad_typing_palm_rejection"
namespace: "input"
description: "Enabling additional touchpad palm rejection will disable the tap to click while the user is typing on a physical keyboard"
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index bed31e2..24c2c74 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -142,7 +142,7 @@
}
AChoreographer* AChoreographer_getInstance() {
- return Choreographer_to_AChoreographer(Choreographer::getForThread());
+ return Choreographer_to_AChoreographer(Choreographer::getForThread().get());
}
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index d98c713..9f64d2c 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -341,17 +341,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {0.f, 0.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 1.f,
.sourceDataspace = kDestDataSpace,
};
@@ -374,16 +374,16 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = mat4(),
.boundaries = rect,
+ .positionTransform = mat4(),
.roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = false,
+ .maxLuminanceNits = 1000.f,
}},
.sourceDataspace = kDestDataSpace,
};
@@ -425,17 +425,17 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = mat4(),
.boundaries = boundary,
- .roundedCornersCrop = rect,
+ .positionTransform = mat4(),
.roundedCornersRadius = {27.f, 27.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = false,
+ .maxLuminanceNits = 1000.f,
}},
.alpha = 1.f,
.sourceDataspace = kDestDataSpace,
@@ -493,17 +493,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {0.f, 0.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 1.f,
.sourceDataspace = kBT2020DataSpace,
};
@@ -531,17 +531,17 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = kScaleAsymmetric,
.boundaries = boundary,
- .roundedCornersCrop = rect,
+ .positionTransform = kScaleAsymmetric,
.roundedCornersRadius = {64.1f, 64.1f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
.isOpaque = true,
+ .maxLuminanceNits = 1000.f,
}},
.alpha = 0.5f,
.sourceDataspace = kBT2020DataSpace,
@@ -560,17 +560,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {50.f, 50.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = true}},
+ .isOpaque = true,
+ .maxLuminanceNits = 1000.f}},
.alpha = 0.5f,
.sourceDataspace = kExtendedHdrDataSpce,
};
@@ -598,17 +598,17 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {50.f, 50.f},
+ .roundedCornersCrop = rect,
},
.source = PixelSource{.buffer = Buffer{.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
.usePremultipliedAlpha = true,
- .isOpaque = false}},
+ .isOpaque = false,
+ .maxLuminanceNits = 1000.f}},
.alpha = 0.5f,
.sourceDataspace = kOtherDataSpace,
};
diff --git a/libs/renderengine/skia/debug/CaptureTimer.cpp b/libs/renderengine/skia/debug/CaptureTimer.cpp
index 11bcdb8..1c1ee0a 100644
--- a/libs/renderengine/skia/debug/CaptureTimer.cpp
+++ b/libs/renderengine/skia/debug/CaptureTimer.cpp
@@ -30,7 +30,7 @@
void CaptureTimer::setTimeout(TimeoutCallback function, std::chrono::milliseconds delay) {
this->clear = false;
- CommonPool::post([=]() {
+ CommonPool::post([=,this]() {
if (this->clear) return;
std::this_thread::sleep_for(delay);
if (this->clear) return;
diff --git a/libs/tracing_perfetto/include/tracing_sdk.h b/libs/tracing_perfetto/include/tracing_sdk.h
index 800bf3c..271d7c8 100644
--- a/libs/tracing_perfetto/include/tracing_sdk.h
+++ b/libs/tracing_perfetto/include/tracing_sdk.h
@@ -292,13 +292,8 @@
arg_ = std::move(arg);
}
- ~DebugArg() {
- free_string_value();
- }
-
void set_value(T value) {
if constexpr (std::is_same_v<T, const char*>) {
- free_string_value();
arg_.value = value;
} else if constexpr (std::is_same_v<T, int64_t>) {
arg_.value = value;
@@ -321,16 +316,6 @@
DISALLOW_COPY_AND_ASSIGN(DebugArg);
TypeMap::type arg_;
const std::string name_;
-
- constexpr void free_string_value() {
- if constexpr (std::is_same_v<typename TypeMap::type,
- PerfettoTeHlExtraDebugArgString>) {
- if (arg_.value) {
- free((void*)arg_.value);
- arg_.value = nullptr;
- }
- }
- }
};
template <typename T>
@@ -375,10 +360,6 @@
arg_ = std::move(arg);
}
- ~ProtoField() {
- free_string_value();
- }
-
void set_value(uint32_t id, T value) {
if constexpr (std::is_same_v<T, int64_t>) {
arg_.header.id = id;
@@ -387,7 +368,6 @@
arg_.header.id = id;
arg_.value = value;
} else if constexpr (std::is_same_v<T, const char*>) {
- free_string_value();
arg_.header.id = id;
arg_.str = value;
}
@@ -404,16 +384,6 @@
private:
DISALLOW_COPY_AND_ASSIGN(ProtoField);
TypeMap::type arg_;
-
- constexpr void free_string_value() {
- if constexpr (std::is_same_v<typename TypeMap::type,
- PerfettoTeHlProtoFieldCstr>) {
- if (arg_.str) {
- free((void*)arg_.str);
- arg_.str = nullptr;
- }
- }
- }
};
class ProtoFieldNested {
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 01287b0..74e354f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -39,6 +39,7 @@
cc_defaults {
name: "libgpuservice_defaults",
defaults: [
+ "aconfig_lib_cc_static_link.defaults",
"gpuservice_defaults",
"libfeatureoverride_deps",
"libgfxstats_deps",
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index f74b4fa..25ee21f 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -25,8 +25,10 @@
#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>
@@ -41,6 +43,7 @@
#include <memory>
namespace gpuservice_flags = com::android::frameworks::gpuservice::flags;
+namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags;
namespace android {
@@ -143,6 +146,14 @@
}
}
+FeatureOverrides GpuService::getFeatureOverrides() {
+ if (!graphicsenv_flags::feature_overrides()) {
+ FeatureOverrides featureOverrides;
+ return featureOverrides;
+ }
+
+ return mFeatureOverrideParser.getFeatureOverrides();
+}
void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
IPCThreadState* ipc = IPCThreadState::self();
@@ -171,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::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);
@@ -235,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) {
@@ -247,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::feature_overrides()) {
+ fprintf(outs,
+ " featureOverrides update and output gpuservice's feature overrides\n");
+ }
fclose(outs);
return NO_ERROR;
}
diff --git a/services/gpuservice/feature_override/FeatureOverrideParser.cpp b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
index 1ad637c..a16bfa8 100644
--- a/services/gpuservice/feature_override/FeatureOverrideParser.cpp
+++ b/services/gpuservice/feature_override/FeatureOverrideParser.cpp
@@ -90,7 +90,6 @@
}
void FeatureOverrideParser::forceFileRead() {
- resetFeatureOverrides(mFeatureOverrides);
mLastProtobufReadTime = 0;
}
@@ -98,6 +97,9 @@
const feature_override::FeatureOverrideProtos overridesProtos = readFeatureConfigProtos(
getFeatureOverrideFilePath());
+ // Clear out the stale values before adding the newly parsed data.
+ resetFeatureOverrides(mFeatureOverrides);
+
// Global feature overrides.
for (const auto &featureConfigProto: overridesProtos.global_features()) {
FeatureConfig featureConfig;
diff --git a/services/gpuservice/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h
index 057d127..22be9a7 100644
--- a/services/gpuservice/include/gpuservice/GpuService.h
+++ b/services/gpuservice/include/gpuservice/GpuService.h
@@ -20,6 +20,7 @@
#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>
@@ -64,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;
@@ -86,6 +88,8 @@
status_t doDump(int fd, const Vector<String16>& args, bool asProto);
+ status_t cmdFeatureOverrides(int out, int /*err*/);
+
/*
* Attributes
*/
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 3140dc8..21f4f2c 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -19,9 +19,11 @@
#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>
@@ -34,37 +36,6 @@
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) {
@@ -165,6 +136,7 @@
mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
mShowTouchesEnabled(false),
mStylusPointerIconEnabled(false),
+ mPointerMotionFilterEnabled(false),
mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
mIsWindowInfoListenerRegistered(false),
mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
@@ -236,15 +208,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);
@@ -322,9 +295,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
@@ -501,6 +476,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,
@@ -638,6 +621,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) {
@@ -973,6 +958,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 =
@@ -1046,6 +1036,21 @@
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(
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index a9d971a..2435125 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -90,6 +90,11 @@
* 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 {
@@ -110,6 +115,7 @@
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;
@@ -168,6 +174,10 @@
const DisplayTopologyPosition sourceBoundary,
int32_t sourceCursorOffsetPx) const REQUIRES(getLock());
+ vec2 filterPointerMotionForAccessibilityLocked(const vec2& current, const vec2& delta,
+ const ui::LogicalDisplayId& displayId)
+ REQUIRES(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.
@@ -228,6 +238,7 @@
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/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4c8147d..ba75071 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>
@@ -935,6 +936,7 @@
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL),
mConnectionManager(mLooper),
+ mTouchStates(mWindowInfos, mConnectionManager),
mNextUnblockedEvent(nullptr),
mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
mDispatchEnabled(false),
@@ -1465,14 +1467,13 @@
std::vector<InputTarget> InputDispatcher::DispatcherTouchState::findOutsideTargets(
ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
- int32_t pointerId, const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos, std::function<void()> dump) {
+ 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 = windowInfos.getWindowHandlesForDisplay(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
@@ -1484,13 +1485,10 @@
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(pointerId);
- DispatcherTouchState::addPointerWindowTarget(windowHandle,
- InputTarget::DispatchMode::OUTSIDE,
- ftl::Flags<InputTarget::Flags>(),
- pointerIds,
- /*firstDownTimeInTarget=*/std::nullopt,
- connections, windowInfos, dump,
- outsideTargets);
+ addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::OUTSIDE,
+ ftl::Flags<InputTarget::Flags>(), pointerIds,
+ /*firstDownTimeInTarget=*/std::nullopt,
+ /*pointerDisplayId=*/std::nullopt, dump, outsideTargets);
}
}
return outsideTargets;
@@ -2052,8 +2050,7 @@
Result<std::vector<InputTarget>, InputEventInjectionResult> result =
mTouchStates
- .findTouchedWindowTargets(currentTime, *entry, mConnectionManager,
- mWindowInfos,
+ .findTouchedWindowTargets(currentTime, *entry,
mDragState ? mDragState->dragWindow : nullptr,
std::bind_front(&InputDispatcher::
addDragEventLocked,
@@ -2373,8 +2370,7 @@
base::Result<std::vector<InputTarget>, os::InputEventInjectionResult>
InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
- nsecs_t currentTime, const MotionEntry& entry, const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos,
+ nsecs_t currentTime, const MotionEntry& entry,
const sp<android::gui::WindowInfoHandle> dragWindow,
std::function<void(const MotionEntry&)> addDragEvent, std::function<void()> dump) {
ATRACE_CALL();
@@ -2387,12 +2383,11 @@
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;
}
@@ -2436,12 +2431,10 @@
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
sp<WindowInfoHandle> newTouchedWindowHandle =
- windowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
if (isDown) {
- targets += DispatcherTouchState::findOutsideTargets(displayId, newTouchedWindowHandle,
- pointer.id, connections,
- windowInfos, dump);
+ targets += findOutsideTargets(displayId, newTouchedWindowHandle, pointer.id, dump);
}
LOG_IF(INFO, newTouchedWindowHandle == nullptr)
<< "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y)
@@ -2454,7 +2447,7 @@
}
std::vector<sp<WindowInfoHandle>> newTouchedWindows =
- findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, windowInfos);
+ 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);
@@ -2467,7 +2460,7 @@
}
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
- if (!canWindowReceiveMotion(windowHandle, entry, connections, windowInfos)) {
+ if (!canWindowReceiveMotion(windowHandle, entry)) {
continue;
}
@@ -2479,8 +2472,7 @@
// Set target flags.
ftl::Flags<InputTarget::Flags> targetFlags =
- DispatcherTouchState::getTargetFlags(windowHandle, {x, y}, isSplit,
- windowInfos);
+ getTargetFlags(windowHandle, {x, y}, isSplit);
// Update the temporary touch state.
@@ -2510,7 +2502,7 @@
windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
sp<WindowInfoHandle> wallpaper =
- windowInfos.findWallpaperWindowBelow(windowHandle);
+ mWindowInfos.findWallpaperWindowBelow(windowHandle);
if (wallpaper != nullptr) {
ftl::Flags<InputTarget::Flags> wallpaperFlags =
InputTarget::Flags::WINDOW_IS_OBSCURED |
@@ -2579,7 +2571,7 @@
tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
sp<WindowInfoHandle> newTouchedWindowHandle =
- windowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
+ mWindowInfos.findTouchedWindowAt(displayId, x, y, isStylus);
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2589,7 +2581,7 @@
// Do not slide events to the window which can not receive motion event
if (newTouchedWindowHandle != nullptr &&
- !canWindowReceiveMotion(newTouchedWindowHandle, entry, connections, windowInfos)) {
+ !canWindowReceiveMotion(newTouchedWindowHandle, entry)) {
newTouchedWindowHandle = nullptr;
}
@@ -2606,18 +2598,16 @@
const TouchedWindow& touchedWindow =
tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
- DispatcherTouchState::
- addPointerWindowTarget(oldTouchedWindowHandle,
- InputTarget::DispatchMode::SLIPPERY_EXIT,
- ftl::Flags<InputTarget::Flags>(), pointerIds,
- touchedWindow.getDownTimeInTarget(entry.deviceId),
- connections, windowInfos, dump, 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.
ftl::Flags<InputTarget::Flags> targetFlags =
- DispatcherTouchState::getTargetFlags(newTouchedWindowHandle, {x, y},
- isSplit, windowInfos);
+ getTargetFlags(newTouchedWindowHandle, {x, y}, isSplit);
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle,
InputTarget::DispatchMode::SLIPPERY_ENTER,
@@ -2625,10 +2615,8 @@
entry.eventTime);
// Check if the wallpaper window should deliver the corresponding event.
- DispatcherTouchState::slipWallpaperTouch(targetFlags, oldTouchedWindowHandle,
- newTouchedWindowHandle, tempTouchState,
- entry, targets, connections, windowInfos,
- dump);
+ slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
+ tempTouchState, entry, targets, dump);
tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
@@ -2660,12 +2648,10 @@
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(entry.pointerProperties[0].id);
for (const TouchedWindow& touchedWindow : hoveringWindows) {
- DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle,
- touchedWindow.dispatchMode,
- touchedWindow.targetFlags, pointerIds,
- touchedWindow.getDownTimeInTarget(
- entry.deviceId),
- connections, windowInfos, dump, targets);
+ addPointerWindowTarget(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ /*pointerDisplayId=*/std::nullopt, dump, targets);
}
}
@@ -2694,7 +2680,7 @@
for (InputTarget& target : targets) {
if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) {
sp<WindowInfoHandle> targetWindow =
- windowInfos.findWindowHandle(target.connection->getToken());
+ mWindowInfos.findWindowHandle(target.connection->getToken());
if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
target.flags |= InputTarget::Flags::ZERO_COORDS;
}
@@ -2718,13 +2704,10 @@
if (touchingPointers.empty()) {
continue;
}
- DispatcherTouchState::addPointerWindowTarget(touchedWindow.windowHandle,
- touchedWindow.dispatchMode,
- touchedWindow.targetFlags,
- getPointerIds(touchingPointers),
- touchedWindow.getDownTimeInTarget(
- entry.deviceId),
- connections, windowInfos, dump, 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
@@ -2779,16 +2762,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;
}
@@ -2811,8 +2790,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;
}
@@ -2931,8 +2911,8 @@
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,
- const ConnectionManager& connections, const DispatcherWindowInfo& windowInfos,
- std::function<void()> dump, std::vector<InputTarget>& inputTargets) {
+ 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;
@@ -2960,14 +2940,15 @@
const WindowInfo& windowInfo = *windowHandle->getInfo();
if (it == inputTargets.end()) {
- std::shared_ptr<Connection> connection = connections.getConnection(windowInfo.token);
+ 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(
createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
- windowInfos.getRawTransform(*windowHandle->getInfo()),
+ mWindowInfos.getRawTransform(*windowHandle->getInfo(),
+ pointerDisplayId),
firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -4167,15 +4148,15 @@
sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
mDragState.reset();
}
- DispatcherTouchState::
- addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS,
- ftl::Flags<InputTarget::Flags>(), pointerIds,
- motionEntry.downTime, mConnectionManager,
- mWindowInfos,
- std::bind_front(&InputDispatcher::
- logDispatchStateLocked,
- this),
- 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);
// Since we don't have a window, use the display transform as the raw transform.
@@ -4235,13 +4216,8 @@
<< "channel '" << connection->getInputChannelName() << "' ~ Synthesized "
<< downEvents.size() << " down events to ensure consistent event stream.";
- auto touchedWindowHandleAndDisplay =
- mTouchStates.findTouchedWindowHandleAndDisplay(connection->getToken());
- if (!touchedWindowHandleAndDisplay.has_value()) {
- LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token";
- }
-
- const auto [windowHandle, displayId] = touchedWindowHandleAndDisplay.value();
+ const auto [windowHandle, displayId] =
+ mTouchStates.findExistingTouchedWindowHandleAndDisplay(connection->getToken());
const bool wasEmpty = connection->outboundQueue.empty();
for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
@@ -4259,14 +4235,14 @@
pointerIndex++) {
pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
}
- DispatcherTouchState::
- addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS,
- targetFlags, pointerIds, motionEntry.downTime,
- mConnectionManager, mWindowInfos,
- std::bind_front(&InputDispatcher::
- logDispatchStateLocked,
- this),
- 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);
// Since we don't have a window, use the display transform as the raw transform.
@@ -5091,8 +5067,19 @@
}
// Only look through the requested display.
- for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(*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;
}
}
@@ -5174,13 +5161,38 @@
}
ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
- const android::gui::WindowInfo& windowInfo) const {
+ 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.
- return (input_flags::use_cloned_screen_coordinates_as_raw() &&
- windowInfo.cloneLayerStackTransform)
- ? *windowInfo.cloneLayerStackTransform
- : getDisplayTransform(windowInfo.displayId);
+ // 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 {
@@ -5213,10 +5225,32 @@
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 ConnectionManager& connections, const DispatcherWindowInfo& windowInfos) const {
+ const android::inputdispatcher::MotionEntry& motionEntry) const {
const WindowInfo& info = *window->getInfo();
// Skip spy window targets that are not valid for targeted injection.
@@ -5235,7 +5269,7 @@
return false;
}
- std::shared_ptr<Connection> connection = connections.getConnection(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());
@@ -5250,8 +5284,8 @@
// Drop events that can't be trusted due to occlusion
const auto [x, y] = resolveTouchedPosition(motionEntry);
DispatcherWindowInfo::TouchOcclusionInfo occlusionInfo =
- windowInfos.computeTouchOcclusionInfo(window, x, y);
- if (!windowInfos.isTouchTrusted(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) {
@@ -5264,7 +5298,7 @@
}
// Drop touch events if requested by input feature
- if (shouldDropInput(motionEntry, window, windowInfos)) {
+ if (shouldDropInput(motionEntry, window, mWindowInfos)) {
return false;
}
@@ -5400,7 +5434,7 @@
CancelationOptions hoverCancellationOptions(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
"WindowInfo changed", traceContext.getTracker());
const std::list<DispatcherTouchState::CancellationArgs> cancellations =
- mTouchStates.updateFromWindowInfo(displayId, mWindowInfos);
+ mTouchStates.updateFromWindowInfo(displayId);
for (const auto& cancellationArgs : cancellations) {
switch (cancellationArgs.mode) {
case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
@@ -5441,26 +5475,25 @@
}
std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
-InputDispatcher::DispatcherTouchState::updateFromWindowInfo(
- ui::LogicalDisplayId displayId, const DispatcherWindowInfo& windowInfos) {
+InputDispatcher::DispatcherTouchState::updateFromWindowInfo(ui::LogicalDisplayId displayId) {
std::list<CancellationArgs> cancellations;
- if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
- TouchState& state = it->second;
- cancellations = eraseRemovedWindowsFromWindowInfo(state, displayId, windowInfos);
+ forTouchAndCursorStatesOnDisplay(displayId, [&](TouchState& state) {
cancellations.splice(cancellations.end(),
- updateHoveringStateFromWindowInfo(state, displayId, windowInfos));
- }
+ 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,
- const DispatcherWindowInfo& windowInfos) {
+ TouchState& state, ui::LogicalDisplayId displayId) {
std::list<CancellationArgs> cancellations;
for (auto it = state.windows.begin(); it != state.windows.end();) {
TouchedWindow& touchedWindow = *it;
- if (windowInfos.isWindowPresent(touchedWindow.windowHandle)) {
+ if (mWindowInfos.isWindowPresent(touchedWindow.windowHandle)) {
it++;
continue;
}
@@ -5486,12 +5519,11 @@
std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>
InputDispatcher::DispatcherTouchState::updateHoveringStateFromWindowInfo(
- TouchState& state, ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos) {
+ 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 = windowInfos.getDisplayTransform(displayId);
+ ui::Transform displayTransform = mWindowInfos.getDisplayTransform(displayId);
for (TouchedWindow& touchedWindow : state.windows) {
std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
[&](const PointerProperties& properties, float x, float y) {
@@ -5746,8 +5778,7 @@
"transferring touch from this window to another window",
traceContext.getTracker());
- auto result = mTouchStates.transferTouchGesture(fromToken, toToken, mWindowInfos,
- mConnectionManager);
+ auto result = mTouchStates.transferTouchGesture(fromToken, toToken);
if (!result.has_value()) {
return false;
}
@@ -5791,9 +5822,7 @@
std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>,
std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>>
InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IBinder>& fromToken,
- const sp<android::IBinder>& toToken,
- const DispatcherWindowInfo& windowInfos,
- const ConnectionManager& connections) {
+ const sp<android::IBinder>& toToken) {
// Find the target touch state and touched window by fromToken.
auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(fromToken);
if (!touchStateWindowAndDisplay.has_value()) {
@@ -5811,7 +5840,10 @@
const DeviceId deviceId = *deviceIds.begin();
const sp<WindowInfoHandle> fromWindowHandle = touchedWindow.windowHandle;
- const sp<WindowInfoHandle> toWindowHandle = windowInfos.findWindowHandle(toToken, displayId);
+ // 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;
@@ -5837,8 +5869,8 @@
deviceId, pointers, downTimeInTarget);
// Synthesize cancel for old window and down for new window.
- std::shared_ptr<Connection> fromConnection = connections.getConnection(fromToken);
- std::shared_ptr<Connection> toConnection = connections.getConnection(toToken);
+ 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) {
@@ -5849,7 +5881,7 @@
// Check if the wallpaper window should deliver the corresponding event.
auto [wallpaperCancellations, wallpaperPointerDowns] =
transferWallpaperTouch(fromWindowHandle, toWindowHandle, state, deviceId, pointers,
- oldTargetFlags, newTargetFlags, windowInfos, connections);
+ oldTargetFlags, newTargetFlags);
cancellations.splice(cancellations.end(), wallpaperCancellations);
pointerDowns.splice(pointerDowns.end(), wallpaperPointerDowns);
@@ -5871,26 +5903,25 @@
*/
sp<WindowInfoHandle> InputDispatcher::DispatcherTouchState::findTouchedForegroundWindow(
ui::LogicalDisplayId displayId) const {
- 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;
}
@@ -7069,8 +7100,7 @@
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, const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos, std::function<void()> dump) {
+ 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];
@@ -7083,19 +7113,17 @@
const sp<WindowInfoHandle> oldWallpaper =
oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
const sp<WindowInfoHandle> newWallpaper =
- newHasWallpaper ? windowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
+ newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(newWindowHandle) : nullptr;
if (oldWallpaper == newWallpaper) {
return;
}
if (oldWallpaper != nullptr) {
const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
- DispatcherTouchState::addPointerWindowTarget(oldWallpaper,
- InputTarget::DispatchMode::SLIPPERY_EXIT,
- oldTouchedWindow.targetFlags,
- getPointerIds(pointers),
- oldTouchedWindow.getDownTimeInTarget(deviceId),
- connections, windowInfos, dump, 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);
}
@@ -7114,8 +7142,7 @@
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 DispatcherWindowInfo& windowInfos,
- const ConnectionManager& connections) {
+ ftl::Flags<InputTarget::Flags> newTargetFlags) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
fromWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -7126,7 +7153,7 @@
const sp<WindowInfoHandle> oldWallpaper =
oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
const sp<WindowInfoHandle> newWallpaper =
- newHasWallpaper ? windowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr;
+ newHasWallpaper ? mWindowInfos.findWallpaperWindowBelow(toWindowHandle) : nullptr;
if (oldWallpaper == newWallpaper) {
return {};
}
@@ -7147,10 +7174,10 @@
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags,
deviceId, pointers, downTimeInTarget);
std::shared_ptr<Connection> wallpaperConnection =
- connections.getConnection(newWallpaper->getToken());
+ mConnectionManager.getConnection(newWallpaper->getToken());
if (wallpaperConnection != nullptr) {
std::shared_ptr<Connection> toConnection =
- connections.getConnection(toWindowHandle->getToken());
+ mConnectionManager.getConnection(toWindowHandle->getToken());
toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
pointerDowns.emplace_back(downTimeInTarget, wallpaperConnection, wallpaperFlags);
}
@@ -7350,9 +7377,12 @@
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,
- const DispatcherWindowInfo& windowInfos) {
+ 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.
@@ -7361,9 +7391,9 @@
if (isSplit) {
targetFlags |= InputTarget::Flags::SPLIT;
}
- if (windowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) {
+ if (mWindowInfos.isWindowObscuredAtPoint(targetWindow, targetPosition.x, targetPosition.y)) {
targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
- } else if (windowInfos.isWindowObscured(targetWindow)) {
+ } else if (mWindowInfos.isWindowObscured(targetWindow)) {
targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
return targetFlags;
@@ -7371,101 +7401,211 @@
bool InputDispatcher::DispatcherTouchState::hasTouchingOrHoveringPointers(
ui::LogicalDisplayId displayId, int32_t deviceId) const {
- const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
- if (touchStateIt == mTouchStatesByDisplay.end()) {
- return false;
- }
- return touchStateIt->second.hasTouchingPointers(deviceId) ||
- touchStateIt->second.hasHoveringPointers(deviceId);
+ 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 {
- const 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;
-}
-
-std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
-InputDispatcher::DispatcherTouchState::findTouchedWindowHandleAndDisplay(
- const sp<android::IBinder>& token) const {
- for (const auto& [displayId, state] : mTouchStatesByDisplay) {
- for (const TouchedWindow& w : state.windows) {
- if (w.windowHandle->getToken() == token) {
- return std::make_tuple(std::ref(w.windowHandle), displayId);
+ 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 std::nullopt;
+ 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 {
- for (const auto& [_, state] : mTouchStatesByDisplay) {
+ 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 {
- const auto touchStateIt = mTouchStatesByDisplay.find(displayId);
- if (touchStateIt == mTouchStatesByDisplay.end()) {
- return;
- }
- for (const TouchedWindow& window : touchStateIt->second.windows) {
- f(window.windowHandle);
- }
+ 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 += StringPrintf("TouchStatesByDisplay:\n");
+ 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 += "TouchStates: <no displays touched>\n";
+ 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) {
- for (auto& [_, touchState] : mTouchStatesByDisplay) {
- touchState.removeAllPointersForDevice(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;
+ }
+
+ // TODO(b/383092013): This is currently not accounting for the "topology group" concept.
+ // Proper implementation requires looking tghrough all the displays in the topology group.
+ const auto cursorStateIt = mCursorStateByDisplay.find(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) {
- for (auto& [displayId, state] : mTouchStatesByDisplay) {
+ 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) {
- return std::make_tuple(std::ref(state), std::ref(w), displayId);
+ touchStateWindowAndDisplay.emplace(std::ref(state), std::ref(w), displayId);
+ return true;
}
}
- }
- return std::nullopt;
+ return false;
+ });
+ return touchStateWindowAndDisplay;
}
bool InputDispatcher::DispatcherTouchState::isStylusActiveInDisplay(
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e76bd89..38f7825 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -307,8 +307,11 @@
// 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.
- ui::Transform getRawTransform(const android::gui::WindowInfo&) 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.
@@ -316,6 +319,11 @@
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.
@@ -336,9 +344,22 @@
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;
@@ -370,20 +391,20 @@
const ftl::Flags<InputTarget::Flags> targetFlags;
};
- static 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,
- const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos,
- std::function<void()> dump,
- std::vector<InputTarget>& inputTargets);
+ 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 ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos,
const sp<android::gui::WindowInfoHandle> dragWindow,
std::function<void(const MotionEntry&)> addDragEvent,
std::function<void()> dump);
@@ -396,9 +417,9 @@
bool isPointerInWindow(const sp<android::IBinder>& token, ui::LogicalDisplayId displayId,
DeviceId deviceId, int32_t pointerId) const;
- // Find touched windowHandle and display by token.
- std::optional<std::tuple<const sp<gui::WindowInfoHandle>&, ui::LogicalDisplayId>>
- findTouchedWindowHandleAndDisplay(const sp<IBinder>& token) 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;
@@ -410,8 +431,7 @@
// Updates the touchState for display from WindowInfo,
// returns list of CancellationArgs for every cancelled touch
- std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos);
+ std::list<CancellationArgs> updateFromWindowInfo(ui::LogicalDisplayId displayId);
void removeAllPointersForDevice(DeviceId deviceId);
@@ -420,9 +440,7 @@
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,
- const DispatcherWindowInfo& windowInfos,
- const ConnectionManager& connections);
+ transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
base::Result<std::list<CancellationArgs>, status_t> pilferPointers(
const sp<IBinder>& token, const Connection& requestingConnection);
@@ -432,6 +450,29 @@
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);
@@ -440,30 +481,31 @@
const sp<gui::WindowInfoHandle> toWindowHandle, TouchState& state,
DeviceId deviceId, const std::vector<PointerProperties>& pointers,
ftl::Flags<InputTarget::Flags> oldTargetFlags,
- ftl::Flags<InputTarget::Flags> newTargetFlags,
- const DispatcherWindowInfo& windowInfos, const ConnectionManager& connections);
+ ftl::Flags<InputTarget::Flags> newTargetFlags);
- bool canWindowReceiveMotion(const sp<android::gui::WindowInfoHandle>& window,
- const MotionEntry& motionEntry,
- const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos) const;
+ 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;
- static std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo(
- TouchState& state, ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos);
+ std::list<CancellationArgs> eraseRemovedWindowsFromWindowInfo(
+ TouchState& state, ui::LogicalDisplayId displayId);
- static std::list<CancellationArgs> updateHoveringStateFromWindowInfo(
- TouchState& state, ui::LogicalDisplayId displayId,
- const DispatcherWindowInfo& windowInfos);
+ std::list<CancellationArgs> updateHoveringStateFromWindowInfo(
+ TouchState& state, ui::LogicalDisplayId displayId);
- static std::vector<InputTarget> findOutsideTargets(
- ui::LogicalDisplayId displayId, const sp<gui::WindowInfoHandle>& touchedWindow,
- int32_t pointerId, const ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos, std::function<void()> dump);
+ 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.
@@ -478,18 +520,18 @@
* @param targets the current targets to add the walpaper ones to
* @param eventTime the new downTime for the wallpaper target
*/
- static 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 ConnectionManager& connections,
- const DispatcherWindowInfo& windowInfos,
- std::function<void()> dump);
+ 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);
- static ftl::Flags<InputTarget::Flags> getTargetFlags(
+ ftl::Flags<InputTarget::Flags> getTargetFlags(
const sp<android::gui::WindowInfoHandle>& targetWindow, vec2 targetPosition,
- bool isSplit, const DispatcherWindowInfo& windowInfos);
+ bool isSplit);
+
+ const DispatcherWindowInfo& mWindowInfos;
+ const ConnectionManager& mConnectionManager;
};
DispatcherTouchState mTouchStates GUARDED_BY(mLock);
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/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/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 3934e78..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",
],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 3c8b6f5..2fcb5d8 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -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,7 +1617,7 @@
}
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());
if (!sysfsRootPathOpt) {
@@ -1628,8 +1634,13 @@
if (!associatedDevice) {
// Found matching associated device for the first time.
associatedDevice = dev->associatedDevice;
- // Reload this associated device if needed.
- const auto reloadedDevice = AssociatedDevice(path);
+ // 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());
@@ -1642,16 +1653,18 @@
if (!associatedDevice) {
// No existing associated device found for this path, so create a new one.
- associatedDevice = std::make_shared<AssociatedDevice>(path);
+ associatedDevice = std::make_shared<AssociatedDevice>(path, config);
}
return associatedDevice;
}
-EventHub::AssociatedDevice::AssociatedDevice(const std::filesystem::path& 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)),
+ lightInfos(readLightsConfiguration(sysfsRootPath, baseDevConfig)),
layoutInfo(readLayoutConfiguration(sysfsRootPath)) {}
std::string EventHub::AssociatedDevice::dump() const {
@@ -2337,11 +2350,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"
@@ -2356,8 +2379,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);
@@ -2664,7 +2687,8 @@
testedDevices.emplace(dev.associatedDevice, false);
return false;
}
- auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath);
+ auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath,
+ dev.associatedDevice->baseDevConfig);
const bool changed = *dev.associatedDevice != reloadedDevice;
testedDevices.emplace(dev.associatedDevice, changed);
return changed;
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 7ab000b..845cab0 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -196,9 +196,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);
}
@@ -263,9 +262,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) {
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 31ac63f..adbfdeb 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>
@@ -87,6 +88,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 +145,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 {
@@ -619,9 +622,12 @@
private:
// Holds information about the sysfs device associated with the Device.
struct AssociatedDevice {
- AssociatedDevice(const std::filesystem::path& sysfsRootPath);
+ 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;
@@ -658,7 +664,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 +678,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 +698,6 @@
void populateAbsoluteAxisStates();
bool hasKeycodeLocked(int keycode) const;
bool hasKeycodeInternalLocked(int keycode) const;
- void loadConfigurationLocked();
bool loadVirtualKeyMapLocked();
status_t loadKeyMapLocked();
bool isExternalDeviceLocked();
@@ -724,7 +729,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);
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/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 18a7102..c982dab 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,15 +50,6 @@
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) {
@@ -470,7 +462,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);
}
@@ -481,7 +473,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.
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/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/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 7cc4ff7..1778f6d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -9924,6 +9924,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);
@@ -9941,82 +9944,81 @@
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(
InputDispatcherUserActivityPokeTests, MinPokeTimeObserved,
REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
rate_limit_user_activity_poke_in_dispatcher))) {
- // Use current time otherwise events may be dropped due to being stale.
- const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
mDispatcher->setMinTimeBetweenUserActivityPokes(50ms);
// First event of type TOUCH. Should poke.
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(50));
+ NotifyMotionArgs motionArgs =
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(50));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + 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,
- currentTime + milliseconds_to_nanoseconds(130));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(130));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + 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,
- currentTime + milliseconds_to_nanoseconds(135));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(135));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + 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,
- currentTime + 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,
- currentTime + 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,
- currentTime + 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,
- currentTime + milliseconds_to_nanoseconds(200));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + 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,
- currentTime + milliseconds_to_nanoseconds(300));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
+ std::chrono::milliseconds(300));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + 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();
@@ -10026,39 +10028,35 @@
InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed,
REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
rate_limit_user_activity_poke_in_dispatcher))) {
- // Use current time otherwise events may be dropped due to being stale.
- const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(200));
+ NotifyMotionArgs motionArgs =
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(200));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + 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,
- currentTime + milliseconds_to_nanoseconds(280));
+ std::chrono::milliseconds(280));
mFakePolicy->assertUserActivityNotPoked();
- notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + milliseconds_to_nanoseconds(340));
+ motionArgs =
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, std::chrono::milliseconds(340));
mFakePolicy->assertUserActivityPoked(
- {{currentTime + milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH,
- ui::LogicalDisplayId::DEFAULT}});
+ {{motionArgs.eventTime, USER_ACTIVITY_EVENT_TOUCH, ui::LogicalDisplayId::DEFAULT}});
}
TEST_F_WITH_FLAGS(
InputDispatcherUserActivityPokeTests, ZeroMinPokeTimeDisablesRateLimiting,
REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
rate_limit_user_activity_poke_in_dispatcher))) {
- // Use current time otherwise events may be dropped due to being stale.
- const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
mDispatcher->setMinTimeBetweenUserActivityPokes(0ms);
notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + 20);
+ std::chrono::milliseconds(20));
mFakePolicy->assertUserActivityPoked();
notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
- currentTime + 30);
+ std::chrono::milliseconds(30));
mFakePolicy->assertUserActivityPoked();
}
@@ -12376,6 +12374,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;
@@ -12396,26 +12399,37 @@
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: {
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: {
- PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(50).y(50);
+ PointerBuilder pointer =
+ PointerBuilder(0, ToolType::STYLUS).x(location.x).y(location.y);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
@@ -12438,12 +12452,14 @@
break;
}
case AINPUT_SOURCE_MOUSE: {
- PointerBuilder pointer =
- PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50);
+ 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(pointer)
.build()));
@@ -12451,6 +12467,7 @@
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)
@@ -12464,43 +12481,59 @@
}
// Window should receive motion event.
- mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ sp<FakeWindowHandle>& targetWindow =
+ displayId == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay;
+ targetWindow->consumeMotionDown(displayId);
if (consumeButtonPress) {
- mWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ targetWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
}
- // Spy window should also receive motion event
- mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+
+ // 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(),
+ mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(),
/*isDragDrop=*/true);
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) {
@@ -15187,4 +15220,187 @@
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();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index dce5472..06d60ce 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -191,6 +191,9 @@
(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 {
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 1286a36..99db8fe 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -1008,6 +1008,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);
+ mChoreographer.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>;
@@ -1776,6 +1804,89 @@
firstMousePc->assertPointerIconNotSet();
}
+TEST_F(PointerChoreographerTest, A11yPointerMotionFilterMouse) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.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}));
+ mChoreographer.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*/>;
diff --git a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
index 767f9cd..43975f0 100644
--- a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
+++ b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
@@ -21,6 +21,14 @@
static constexpr int32_t MAX_RANDOM_POINTERS = 4;
static constexpr int32_t MAX_RANDOM_DEVICES = 4;
+// 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>({
AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_ACTION_UP, AMOTION_EVENT_ACTION_MOVE,
@@ -185,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/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 846260a..bba7389 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;
@@ -367,8 +395,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/stats/OWNERS b/services/stats/OWNERS
index a61babf..a599619 100644
--- a/services/stats/OWNERS
+++ b/services/stats/OWNERS
@@ -6,4 +6,3 @@
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
-yro@google.com
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/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index c743ea2..e8b09b0 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -223,9 +223,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);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 23b3637..b5a543a 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -98,7 +98,7 @@
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);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 1514340..1f0d5d0 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -305,7 +305,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()) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e1bba44..95a7170 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -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;
}
@@ -842,7 +845,7 @@
return true;
}
-void Layer::releasePreviousBuffer() {
+void Layer::releasePreviousBuffer() REQUIRES(mFlinger->mStateLock) {
mReleasePreviousBuffer = true;
if (!mBufferInfo.mBuffer ||
(!mDrawingState.buffer->hasSameBuffer(*mBufferInfo.mBuffer) ||
@@ -884,7 +887,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 =
@@ -1074,7 +1078,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) {
@@ -1207,7 +1212,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) {
@@ -1457,7 +1462,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/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 18bf416..a91dde7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -144,7 +144,7 @@
// 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);
+ virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) = 0;
};
struct IEventThreadCallback {
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/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 574d39a..afbe20d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1363,7 +1363,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 */);
@@ -1382,6 +1383,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:
@@ -1460,19 +1470,25 @@
}
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();
- // The DisplayDevice has been destroyed, so abort the commit for the now dead FrameTargeter.
- return false;
+ // The DisplayDevice has been destroyed, so abort the commit for the now dead
+ // FrameTargeter.
+ return false;
+ }
}
mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
@@ -1480,7 +1496,8 @@
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);
}
@@ -1532,8 +1549,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;
}
@@ -1554,6 +1572,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);
@@ -2456,7 +2483,8 @@
}
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");
@@ -2653,7 +2681,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();
@@ -3459,13 +3487,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 =
@@ -4130,6 +4152,35 @@
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)) {
@@ -4141,13 +4192,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();
}
}
}
@@ -4847,12 +4894,14 @@
return applyTransactions(transactions);
}
-bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactions(std::vector<QueuedTransactionState>& transactions)
+ EXCLUDES(mStateLock) {
Mutex::Autolock lock(mStateLock);
return applyTransactionsLocked(transactions);
}
-bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<QueuedTransactionState>& transactions)
+ REQUIRES(mStateLock) {
bool needsTraversal = false;
// Now apply all transactions.
for (auto& transaction : transactions) {
@@ -5069,15 +5118,13 @@
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,
+ 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) REQUIRES(mStateLock) {
uint32_t transactionFlags = 0;
// start and end registration for listeners w/ no surface so they can get their callback. Note
@@ -5229,7 +5276,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;
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index b1552e6..f9aba9f 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -171,6 +171,7 @@
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(use_known_refresh_rate_for_fps_consistency);
@@ -295,6 +296,7 @@
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, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 073302e..de3f359 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -107,6 +107,7 @@
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 use_known_refresh_rate_for_fps_consistency() const;
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 96ab7ab..fa1da45 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -265,6 +265,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"
diff --git a/vulkan/tests/Android.bp b/vulkan/tests/Android.bp
new file mode 100644
index 0000000..551d9b7
--- /dev/null
+++ b/vulkan/tests/Android.bp
@@ -0,0 +1,56 @@
+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: "libvulkan_test",
+ test_suites: ["general-tests"],
+
+ srcs: [
+ "libvulkan_test.cpp",
+ ],
+
+ strip: {
+ none: true,
+ },
+
+ cflags: [
+ "-DVK_USE_PLATFORM_ANDROID_KHR",
+ "-Wall",
+ "-Werror",
+ ],
+
+ header_libs: [
+ "hwvulkan_headers",
+ "vulkan_headers",
+ ],
+
+ cppflags: [
+ "-Wno-c++98-compat-pedantic",
+ "-Wno-c99-extensions",
+ "-Wno-exit-time-destructors",
+ "-Wno-float-equal",
+ "-Wno-global-constructors",
+ "-Wno-zero-length-array",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libgraphicsenv",
+ "liblog",
+ "libmediandk",
+ "libvulkan",
+ ],
+
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ "liblog",
+ ],
+
+}
diff --git a/vulkan/tests/README.md b/vulkan/tests/README.md
new file mode 100644
index 0000000..3c9b66c
--- /dev/null
+++ b/vulkan/tests/README.md
@@ -0,0 +1,24 @@
+#libvulkan_test
+
+This binary contains the unit tests for testing libvulkan (The Vulkan Loader).
+
+These tests rely on the underlying GPU driver to be able to successfully create a valid
+swapchain. These tests are design to run on an Android emulator to give us a consistent GPU
+driver to test against. YMMV when running this on a physical device with an arbitrary GPU
+driver.
+
+To run these tests run:
+```
+atest libvulkan_test
+```
+
+If using an acloud device the full command list for the root of a freshly cloned repo would be:
+```
+source build/envsetup.sh
+lunch aosp_cf_x86_64_phone-trunk_staging-eng
+m
+acloud create --local-image
+atest libvulkan_test
+```
+
+
diff --git a/vulkan/tests/libvulkan_test.cpp b/vulkan/tests/libvulkan_test.cpp
new file mode 100644
index 0000000..128d640
--- /dev/null
+++ b/vulkan/tests/libvulkan_test.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <media/NdkImageReader.h>
+#include <vulkan/vulkan.h>
+
+#define LOGI(...) \
+ __android_log_print(ANDROID_LOG_INFO, "libvulkan_test", __VA_ARGS__)
+#define LOGE(...) \
+ __android_log_print(ANDROID_LOG_ERROR, "libvulkan_test", __VA_ARGS__)
+
+#define VK_CHECK(result) ASSERT_EQ(VK_SUCCESS, result)
+
+namespace android {
+
+class AImageReaderVulkanSwapchainTest : public ::testing::Test {
+ public:
+ AImageReaderVulkanSwapchainTest() {}
+
+ AImageReader* mReader = nullptr;
+ ANativeWindow* mWindow = nullptr;
+ VkInstance mVkInstance = VK_NULL_HANDLE;
+ VkPhysicalDevice mPhysicalDev = VK_NULL_HANDLE;
+ VkDevice mDevice = VK_NULL_HANDLE;
+ VkSurfaceKHR mSurface = VK_NULL_HANDLE;
+ VkQueue mPresentQueue = VK_NULL_HANDLE;
+ uint32_t mPresentQueueFamily = UINT32_MAX;
+ VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ // ------------------------------------------------------
+ // Helper methods
+ // ------------------------------------------------------
+
+ void createVulkanInstance(std::vector<const char*>& layers) {
+ const char* extensions[] = {
+ VK_KHR_SURFACE_EXTENSION_NAME,
+ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
+ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
+ };
+
+ VkApplicationInfo appInfo{};
+ appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ appInfo.pApplicationName = "AImageReader Vulkan Swapchain Test";
+ appInfo.applicationVersion = 1;
+ appInfo.pEngineName = "TestEngine";
+ appInfo.engineVersion = 1;
+ appInfo.apiVersion = VK_API_VERSION_1_0;
+
+ VkInstanceCreateInfo instInfo{};
+ instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ instInfo.pApplicationInfo = &appInfo;
+ instInfo.enabledExtensionCount =
+ sizeof(extensions) / sizeof(extensions[0]);
+ instInfo.ppEnabledExtensionNames = extensions;
+ instInfo.enabledLayerCount = layers.size();
+ instInfo.ppEnabledLayerNames = layers.data();
+ VkResult res = vkCreateInstance(&instInfo, nullptr, &mVkInstance);
+ VK_CHECK(res);
+ LOGE("Vulkan instance created");
+ }
+
+ void createAImageReader(int width, int height, int format, int maxImages) {
+ media_status_t status =
+ AImageReader_new(width, height, format, maxImages, &mReader);
+ ASSERT_EQ(AMEDIA_OK, status) << "Failed to create AImageReader";
+ ASSERT_NE(nullptr, mReader) << "AImageReader is null";
+
+ // Optionally set a listener
+ AImageReader_ImageListener listener{};
+ listener.context = this;
+ listener.onImageAvailable =
+ &AImageReaderVulkanSwapchainTest::onImageAvailable;
+ AImageReader_setImageListener(mReader, &listener);
+
+ LOGI("AImageReader created with %dx%d, format=%d", width, height,
+ format);
+ }
+
+ void getANativeWindowFromReader() {
+ ASSERT_NE(nullptr, mReader);
+
+ media_status_t status = AImageReader_getWindow(mReader, &mWindow);
+ ASSERT_EQ(AMEDIA_OK, status)
+ << "Failed to get ANativeWindow from AImageReader";
+ ASSERT_NE(nullptr, mWindow) << "ANativeWindow is null";
+ LOGI("ANativeWindow obtained from AImageReader");
+ }
+
+ void createVulkanSurface() {
+ ASSERT_NE((VkInstance)VK_NULL_HANDLE, mVkInstance);
+ ASSERT_NE((ANativeWindow*)nullptr, mWindow);
+
+ VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo{};
+ surfaceCreateInfo.sType =
+ VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.window = mWindow;
+
+ VkResult res = vkCreateAndroidSurfaceKHR(
+ mVkInstance, &surfaceCreateInfo, nullptr, &mSurface);
+ VK_CHECK(res);
+ LOGI("Vulkan surface created from ANativeWindow");
+ }
+
+ void pickPhysicalDeviceAndQueueFamily() {
+ ASSERT_NE((VkInstance)VK_NULL_HANDLE, mVkInstance);
+
+ uint32_t deviceCount = 0;
+ vkEnumeratePhysicalDevices(mVkInstance, &deviceCount, nullptr);
+ ASSERT_GT(deviceCount, 0U) << "No Vulkan physical devices found!";
+
+ std::vector<VkPhysicalDevice> devices(deviceCount);
+ vkEnumeratePhysicalDevices(mVkInstance, &deviceCount, devices.data());
+
+ for (auto& dev : devices) {
+ uint32_t queueFamilyCount = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(dev, &queueFamilyCount,
+ nullptr);
+ std::vector<VkQueueFamilyProperties> queueProps(queueFamilyCount);
+ vkGetPhysicalDeviceQueueFamilyProperties(dev, &queueFamilyCount,
+ queueProps.data());
+
+ for (uint32_t i = 0; i < queueFamilyCount; i++) {
+ VkBool32 support = VK_FALSE;
+ vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, mSurface,
+ &support);
+ if (support == VK_TRUE) {
+ // Found a queue family that can present
+ mPhysicalDev = dev;
+ mPresentQueueFamily = i;
+
+ LOGI(
+ "Physical device found with queue family %u supporting "
+ "present",
+ i);
+ return;
+ }
+ }
+ }
+
+ FAIL()
+ << "No physical device found that supports present to the surface!";
+ }
+
+ void createDeviceAndGetQueue(std::vector<const char*>& layers) {
+ ASSERT_NE((void*)VK_NULL_HANDLE, mPhysicalDev);
+ ASSERT_NE(UINT32_MAX, mPresentQueueFamily);
+
+ float queuePriority = 1.0f;
+ VkDeviceQueueCreateInfo queueInfo{};
+ queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ queueInfo.queueFamilyIndex = mPresentQueueFamily;
+ queueInfo.queueCount = 1;
+ queueInfo.pQueuePriorities = &queuePriority;
+
+ VkDeviceCreateInfo deviceInfo{};
+ deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
+ deviceInfo.queueCreateInfoCount = 1;
+ deviceInfo.pQueueCreateInfos = &queueInfo;
+ deviceInfo.enabledLayerCount = layers.size();
+ deviceInfo.ppEnabledLayerNames = layers.data();
+
+ const char* extensions[] = {
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ };
+ deviceInfo.enabledExtensionCount =
+ sizeof(extensions) / sizeof(extensions[0]);
+ deviceInfo.ppEnabledExtensionNames = extensions;
+
+ VkResult res =
+ vkCreateDevice(mPhysicalDev, &deviceInfo, nullptr, &mDevice);
+ VK_CHECK(res);
+ LOGI("Logical device created");
+
+ vkGetDeviceQueue(mDevice, mPresentQueueFamily, 0, &mPresentQueue);
+ ASSERT_NE((VkQueue)VK_NULL_HANDLE, mPresentQueue);
+ LOGI("Acquired present-capable queue");
+ }
+
+ void createSwapchain() {
+ ASSERT_NE((VkDevice)VK_NULL_HANDLE, mDevice);
+ ASSERT_NE((VkSurfaceKHR)VK_NULL_HANDLE, mSurface);
+
+ VkSurfaceCapabilitiesKHR surfaceCaps{};
+ VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
+ mPhysicalDev, mSurface, &surfaceCaps));
+
+ uint32_t formatCount = 0;
+ vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface,
+ &formatCount, nullptr);
+ ASSERT_GT(formatCount, 0U);
+ std::vector<VkSurfaceFormatKHR> formats(formatCount);
+ vkGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDev, mSurface,
+ &formatCount, formats.data());
+
+ VkSurfaceFormatKHR chosenFormat = formats[0];
+ LOGI("Chosen surface format: %d", chosenFormat.format);
+
+ uint32_t presentModeCount = 0;
+ vkGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDev, mSurface,
+ &presentModeCount, nullptr);
+ ASSERT_GT(presentModeCount, 0U);
+ std::vector<VkPresentModeKHR> presentModes(presentModeCount);
+ vkGetPhysicalDeviceSurfacePresentModesKHR(
+ mPhysicalDev, mSurface, &presentModeCount, presentModes.data());
+
+ VkPresentModeKHR chosenPresentMode = VK_PRESENT_MODE_FIFO_KHR;
+ for (auto mode : presentModes) {
+ if (mode == VK_PRESENT_MODE_FIFO_KHR) {
+ chosenPresentMode = mode;
+ break;
+ }
+ }
+ LOGI("Chosen present mode: %d", chosenPresentMode);
+
+ VkExtent2D swapchainExtent{};
+ if (surfaceCaps.currentExtent.width == 0xFFFFFFFF) {
+ swapchainExtent.width = 640; // fallback
+ swapchainExtent.height = 480; // fallback
+ } else {
+ swapchainExtent = surfaceCaps.currentExtent;
+ }
+ LOGI("Swapchain extent: %d x %d", swapchainExtent.width,
+ swapchainExtent.height);
+
+ uint32_t desiredImageCount = surfaceCaps.minImageCount + 1;
+ if (surfaceCaps.maxImageCount > 0 &&
+ desiredImageCount > surfaceCaps.maxImageCount) {
+ desiredImageCount = surfaceCaps.maxImageCount;
+ }
+
+ VkSwapchainCreateInfoKHR swapchainInfo{};
+ swapchainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapchainInfo.surface = mSurface;
+ swapchainInfo.minImageCount = desiredImageCount;
+ swapchainInfo.imageFormat = chosenFormat.format;
+ swapchainInfo.imageColorSpace = chosenFormat.colorSpace;
+ swapchainInfo.imageExtent = swapchainExtent;
+ swapchainInfo.imageArrayLayers = 1;
+ swapchainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ swapchainInfo.preTransform = surfaceCaps.currentTransform;
+ swapchainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
+ swapchainInfo.presentMode = chosenPresentMode;
+ swapchainInfo.clipped = VK_TRUE;
+ swapchainInfo.oldSwapchain = VK_NULL_HANDLE;
+
+ uint32_t queueFamilyIndices[] = {mPresentQueueFamily};
+ swapchainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchainInfo.queueFamilyIndexCount = 1;
+ swapchainInfo.pQueueFamilyIndices = queueFamilyIndices;
+
+ VkResult res =
+ vkCreateSwapchainKHR(mDevice, &swapchainInfo, nullptr, &mSwapchain);
+ VK_CHECK(res);
+ LOGI("Swapchain created successfully");
+
+ uint32_t swapchainImageCount = 0;
+ vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount,
+ nullptr);
+ std::vector<VkImage> swapchainImages(swapchainImageCount);
+ vkGetSwapchainImagesKHR(mDevice, mSwapchain, &swapchainImageCount,
+ swapchainImages.data());
+
+ LOGI("Swapchain has %u images", swapchainImageCount);
+ }
+
+ // Image available callback (AImageReader)
+ static void onImageAvailable(void*, AImageReader* reader) {
+ LOGI("onImageAvailable callback triggered");
+ AImage* image = nullptr;
+ media_status_t status = AImageReader_acquireLatestImage(reader, &image);
+ if (status != AMEDIA_OK || !image) {
+ LOGE("Failed to acquire latest image");
+ return;
+ }
+ AImage_delete(image);
+ LOGI("Released acquired image");
+ }
+
+ void cleanUpSwapchainForTest() {
+ if (mSwapchain != VK_NULL_HANDLE) {
+ vkDestroySwapchainKHR(mDevice, mSwapchain, nullptr);
+ mSwapchain = VK_NULL_HANDLE;
+ }
+ if (mDevice != VK_NULL_HANDLE) {
+ vkDestroyDevice(mDevice, nullptr);
+ mDevice = VK_NULL_HANDLE;
+ }
+ if (mSurface != VK_NULL_HANDLE) {
+ vkDestroySurfaceKHR(mVkInstance, mSurface, nullptr);
+ mSurface = VK_NULL_HANDLE;
+ }
+ if (mVkInstance != VK_NULL_HANDLE) {
+ vkDestroyInstance(mVkInstance, nullptr);
+ mVkInstance = VK_NULL_HANDLE;
+ }
+ if (mReader) {
+ // AImageReader_delete(mReader);
+ mReader = nullptr;
+ }
+ // Note: The ANativeWindow from AImageReader is implicitly
+ // managed by the reader, so we don't explicitly delete it.
+ mWindow = nullptr;
+ }
+
+ void buildSwapchianForTest(std::vector<const char*>& instanceLayers,
+ std::vector<const char*>& deviceLayers) {
+ createVulkanInstance(instanceLayers);
+
+ // the "atest libvulkan_test" command will execute this test as a binary
+ // (not apk) on the device. Consequently we can't render to the screen
+ // and need to work around this by using AImageReader*
+ createAImageReader(640, 480, AIMAGE_FORMAT_PRIVATE, 3);
+ getANativeWindowFromReader();
+ createVulkanSurface();
+ pickPhysicalDeviceAndQueueFamily();
+
+ createDeviceAndGetQueue(deviceLayers);
+ createSwapchain();
+ }
+};
+
+TEST_F(AImageReaderVulkanSwapchainTest, TestHelperMethods) {
+ // Verify that the basic plumbing/helper functions of these tests is
+ // working. This doesn't directly test any of the layer code. It only
+ // verifies that we can successfully create a swapchain with an AImageReader
+
+ std::vector<const char*> instanceLayers;
+ std::vector<const char*> deviceLayers;
+ buildSwapchianForTest(deviceLayers, instanceLayers);
+
+ ASSERT_NE(mVkInstance, (VkInstance)VK_NULL_HANDLE);
+ ASSERT_NE(mPhysicalDev, (VkPhysicalDevice)VK_NULL_HANDLE);
+ ASSERT_NE(mDevice, (VkDevice)VK_NULL_HANDLE);
+ ASSERT_NE(mSurface, (VkSurfaceKHR)VK_NULL_HANDLE);
+ ASSERT_NE(mSwapchain, (VkSwapchainKHR)VK_NULL_HANDLE);
+ cleanUpSwapchainForTest();
+}
+
+} // namespace android