Merge "dumpstate: attach more than 1 trace to BR" into main
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/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/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/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/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/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..62e2d1a 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,7 +146,6 @@
}
}
-
void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
@@ -171,7 +173,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 +241,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 +258,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/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h
index 057d127..116b6d7 100644
--- a/services/gpuservice/include/gpuservice/GpuService.h
+++ b/services/gpuservice/include/gpuservice/GpuService.h
@@ -86,6 +86,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..85f842c 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>
@@ -165,6 +167,7 @@
mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
mShowTouchesEnabled(false),
mStylusPointerIconEnabled(false),
+ mPointerMotionFilterEnabled(false),
mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
mIsWindowInfoListenerRegistered(false),
mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
@@ -322,9 +325,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
@@ -638,6 +643,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 +980,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 +1058,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 bc2904e..a77dc43 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>
@@ -165,8 +166,6 @@
constexpr int LOGTAG_INPUT_FOCUS = 62001;
constexpr int LOGTAG_INPUT_CANCEL = 62003;
-static const bool USE_TOPOLOGY = com::android::input::flags::connected_displays_cursor();
-
const ui::Transform kIdentityTransform;
inline nsecs_t now() {
@@ -1493,13 +1492,11 @@
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, connections, windowInfos,
+ dump, outsideTargets);
}
}
return outsideTargets;
@@ -2398,7 +2395,7 @@
// Copy current touch state into tempTouchState.
// 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 = getTouchStateForMotionEntry(entry);
+ const TouchState* oldState = getTouchStateForMotionEntry(entry, windowInfos);
TouchState tempTouchState;
if (oldState != nullptr) {
tempTouchState = *oldState;
@@ -2614,12 +2611,12 @@
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, connections, windowInfos,
+ dump, targets);
// Make a slippery entrance into the new window.
@@ -2668,12 +2665,11 @@
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, connections, windowInfos,
+ dump, targets);
}
}
@@ -2726,13 +2722,11 @@
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, connections, windowInfos, dump,
+ targets);
}
// During targeted injection, only allow owned targets to receive events
@@ -2787,9 +2781,9 @@
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
if (displayId >= ui::LogicalDisplayId::DEFAULT) {
tempTouchState.clearWindowsWithoutPointers();
- saveTouchStateForMotionEntry(entry, std::move(tempTouchState));
+ saveTouchStateForMotionEntry(entry, std::move(tempTouchState), windowInfos);
} else {
- eraseTouchStateForMotionEntry(entry);
+ eraseTouchStateForMotionEntry(entry, windowInfos);
}
}
@@ -2815,8 +2809,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;
}
@@ -2935,8 +2930,9 @@
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, const ConnectionManager& connections,
+ const DispatcherWindowInfo& windowInfos, std::function<void()> dump,
+ std::vector<InputTarget>& inputTargets) {
if (pointerIds.none()) {
for (const auto& target : inputTargets) {
LOG(INFO) << "Target: " << target;
@@ -2971,7 +2967,8 @@
}
inputTargets.push_back(
createInputTarget(connection, windowHandle, dispatchMode, targetFlags,
- windowInfos.getRawTransform(*windowHandle->getInfo()),
+ windowInfos.getRawTransform(*windowHandle->getInfo(),
+ pointerDisplayId),
firstDownTimeInTarget));
it = inputTargets.end() - 1;
}
@@ -4174,8 +4171,9 @@
DispatcherTouchState::
addPointerWindowTarget(window, InputTarget::DispatchMode::AS_IS,
ftl::Flags<InputTarget::Flags>(), pointerIds,
- motionEntry.downTime, mConnectionManager,
- mWindowInfos,
+ motionEntry.downTime,
+ /*pointerDisplayId=*/std::nullopt,
+ mConnectionManager, mWindowInfos,
std::bind_front(&InputDispatcher::
logDispatchStateLocked,
this),
@@ -4261,6 +4259,7 @@
DispatcherTouchState::
addPointerWindowTarget(windowHandle, InputTarget::DispatchMode::AS_IS,
targetFlags, pointerIds, motionEntry.downTime,
+ /*pointerDisplayId=*/std::nullopt,
mConnectionManager, mWindowInfos,
std::bind_front(&InputDispatcher::
logDispatchStateLocked,
@@ -5173,13 +5172,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 {
@@ -7094,7 +7118,8 @@
oldTouchedWindow.targetFlags,
getPointerIds(pointers),
oldTouchedWindow.getDownTimeInTarget(deviceId),
- connections, windowInfos, dump, targets);
+ /*pointerDisplayId=*/std::nullopt, connections,
+ windowInfos, dump, targets);
state.removeTouchingPointerFromWindow(deviceId, pointerProperties.id, oldWallpaper);
}
@@ -7475,32 +7500,37 @@
void InputDispatcher::DispatcherTouchState::saveTouchStateForMotionEntry(
const android::inputdispatcher::MotionEntry& entry,
- android::inputdispatcher::TouchState&& touchState) {
+ android::inputdispatcher::TouchState&& touchState,
+ const DispatcherWindowInfo& windowInfos) {
if (touchState.windows.empty()) {
- eraseTouchStateForMotionEntry(entry);
+ eraseTouchStateForMotionEntry(entry, windowInfos);
return;
}
- if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
- mCursorStateByDisplay[entry.displayId] = std::move(touchState);
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ mCursorStateByDisplay[windowInfos.getPrimaryDisplayId(entry.displayId)] =
+ std::move(touchState);
} else {
mTouchStatesByDisplay[entry.displayId] = std::move(touchState);
}
}
void InputDispatcher::DispatcherTouchState::eraseTouchStateForMotionEntry(
- const android::inputdispatcher::MotionEntry& entry) {
- if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
- mCursorStateByDisplay.erase(entry.displayId);
+ const android::inputdispatcher::MotionEntry& entry,
+ const DispatcherWindowInfo& windowInfos) {
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ mCursorStateByDisplay.erase(windowInfos.getPrimaryDisplayId(entry.displayId));
} else {
mTouchStatesByDisplay.erase(entry.displayId);
}
}
const TouchState* InputDispatcher::DispatcherTouchState::getTouchStateForMotionEntry(
- const android::inputdispatcher::MotionEntry& entry) const {
- if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
- auto touchStateIt = mCursorStateByDisplay.find(entry.displayId);
+ const android::inputdispatcher::MotionEntry& entry,
+ const DispatcherWindowInfo& windowInfos) const {
+ if (InputFlags::connectedDisplaysCursorEnabled() && isMouseOrTouchpad(entry.source)) {
+ auto touchStateIt =
+ mCursorStateByDisplay.find(windowInfos.getPrimaryDisplayId(entry.displayId));
if (touchStateIt != mCursorStateByDisplay.end()) {
return &touchStateIt->second;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index c2224de..7e8e142 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.
@@ -336,6 +339,13 @@
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:
@@ -375,6 +385,7 @@
ftl::Flags<InputTarget::Flags> targetFlags,
std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
+ std::optional<ui::LogicalDisplayId> pointerDisplayId,
const ConnectionManager& connections,
const DispatcherWindowInfo& windowInfos,
std::function<void()> dump,
@@ -466,12 +477,15 @@
ftl::Flags<InputTarget::Flags> newTargetFlags,
const DispatcherWindowInfo& windowInfos, const ConnectionManager& connections);
- void saveTouchStateForMotionEntry(const MotionEntry& entry, TouchState&& touchState);
+ void saveTouchStateForMotionEntry(const MotionEntry& entry, TouchState&& touchState,
+ const DispatcherWindowInfo& windowInfos);
- void eraseTouchStateForMotionEntry(const MotionEntry& entry);
+ void eraseTouchStateForMotionEntry(const MotionEntry& entry,
+ const DispatcherWindowInfo& windowInfos);
const TouchState* getTouchStateForMotionEntry(
- const android::inputdispatcher::MotionEntry& entry) const;
+ const android::inputdispatcher::MotionEntry& entry,
+ const DispatcherWindowInfo& windowInfos) const;
bool canWindowReceiveMotion(const sp<gui::WindowInfoHandle>& window,
const MotionEntry& motionEntry,
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 9b5a79b..d21c4d7 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -16,6 +16,7 @@
#include "DebugConfig.h"
#include "input/InputDevice.h"
+#include "input/InputFlags.h"
#include "InputState.h"
@@ -24,6 +25,17 @@
namespace android::inputdispatcher {
+namespace {
+
+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));
+}
+
+} // namespace
+
InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}
InputState::~InputState() {}
@@ -221,10 +233,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 +355,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/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/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 31ac63f..5dce074 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>
@@ -619,9 +620,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 +662,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 +676,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 +696,6 @@
void populateAbsoluteAxisStates();
bool hasKeycodeLocked(int keycode) const;
bool hasKeycodeInternalLocked(int keycode) const;
- void loadConfigurationLocked();
bool loadVirtualKeyMapLocked();
status_t loadKeyMapLocked();
bool isExternalDeviceLocked();
@@ -724,7 +727,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/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e0a4afb..6c5d94d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -12374,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;
@@ -12394,10 +12399,17 @@
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});
}
@@ -12482,11 +12494,12 @@
mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow",
ui::LogicalDisplayId::DEFAULT);
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});
// Transfer touch focus to the drag window
bool transferred =
@@ -12499,6 +12512,13 @@
}
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) {
@@ -15185,4 +15205,145 @@
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, MultiDisplayMouseDragAndDrop) {
+ SCOPED_FLAG_OVERRIDE(connected_displays_cursor, 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();
+}
+
} // 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..d02c3d9 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -1776,6 +1776,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/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/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 {