Merge "Fix -Wdefaulted-function-deleted warnings."
diff --git a/Android.bp b/Android.bp
index dec6716..615a7a8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -64,14 +64,20 @@
name: "framework_native_aidl_binder",
srcs: ["aidl/binder/**/*.aidl"],
path: "aidl/binder",
- visibility: ["//frameworks/native"],
+ visibility: [
+ "//frameworks/native",
+ "//frameworks/native/libs/gui",
+ ],
}
filegroup {
name: "framework_native_aidl_gui",
srcs: ["aidl/gui/**/*.aidl"],
path: "aidl/gui",
- visibility: ["//frameworks/native"],
+ visibility: [
+ "//frameworks/native",
+ "//frameworks/native/libs/gui",
+ ],
}
filegroup {
@@ -82,7 +88,7 @@
],
}
-cc_library_headers{
+cc_library_headers {
name: "libandroid_headers_private",
export_include_dirs: ["include/private"],
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index e6dbd79..d347262 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -36,6 +36,7 @@
#include <utils/Compat.h>
#include <utils/String8.h>
+#include "BuildFlags.h"
#include "FdTrigger.h"
#include "OS.h"
#include "RpcSocketAddress.h"
@@ -78,10 +79,8 @@
void RpcSession::setMaxIncomingThreads(size_t threads) {
RpcMutexLockGuard _l(mMutex);
- LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(),
- "Must set max incoming threads before setting up connections, but has %zu "
- "client(s) and %zu server(s)",
- mConnections.mOutgoing.size(), mConnections.mIncoming.size());
+ LOG_ALWAYS_FATAL_IF(mStartedSetup,
+ "Must set max incoming threads before setting up connections");
mMaxIncomingThreads = threads;
}
@@ -92,10 +91,8 @@
void RpcSession::setMaxOutgoingThreads(size_t threads) {
RpcMutexLockGuard _l(mMutex);
- LOG_ALWAYS_FATAL_IF(!mConnections.mOutgoing.empty() || !mConnections.mIncoming.empty(),
- "Must set max outgoing threads before setting up connections, but has %zu "
- "client(s) and %zu server(s)",
- mConnections.mOutgoing.size(), mConnections.mIncoming.size());
+ LOG_ALWAYS_FATAL_IF(mStartedSetup,
+ "Must set max outgoing threads before setting up connections");
mMaxOutgoingThreads = threads;
}
@@ -104,7 +101,7 @@
return mMaxOutgoingThreads;
}
-bool RpcSession::setProtocolVersion(uint32_t version) {
+bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) {
if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT &&
version != RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) {
ALOGE("Cannot start RPC session with version %u which is unknown (current protocol version "
@@ -114,6 +111,8 @@
}
RpcMutexLockGuard _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(checkStarted && mStartedSetup,
+ "Must set protocol version before setting up connections");
if (mProtocolVersion && version > *mProtocolVersion) {
ALOGE("Cannot upgrade explicitly capped protocol version %u to newer version %u",
*mProtocolVersion, version);
@@ -124,12 +123,19 @@
return true;
}
+bool RpcSession::setProtocolVersion(uint32_t version) {
+ return setProtocolVersionInternal(version, true);
+}
+
std::optional<uint32_t> RpcSession::getProtocolVersion() {
RpcMutexLockGuard _l(mMutex);
return mProtocolVersion;
}
void RpcSession::setFileDescriptorTransportMode(FileDescriptorTransportMode mode) {
+ RpcMutexLockGuard _l(mMutex);
+ LOG_ALWAYS_FATAL_IF(mStartedSetup,
+ "Must set file descriptor transport mode before setting up connections");
mFileDescriptorTransportMode = mode;
}
@@ -445,9 +451,16 @@
bool incoming)>& connectAndInit) {
{
RpcMutexLockGuard _l(mMutex);
- LOG_ALWAYS_FATAL_IF(mConnections.mOutgoing.size() != 0,
- "Must only setup session once, but already has %zu clients",
- mConnections.mOutgoing.size());
+ LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must only setup session once");
+ mStartedSetup = true;
+
+ if constexpr (!kEnableRpcThreads) {
+ LOG_ALWAYS_FATAL_IF(mMaxIncomingThreads > 0,
+ "Incoming threads are not supported on single-threaded libbinder");
+ // mMaxIncomingThreads should not change from here to its use below,
+ // since we set mStartedSetup==true and setMaxIncomingThreads checks
+ // for that
+ }
}
if (auto status = initShutdownTrigger(); status != OK) return status;
@@ -488,7 +501,7 @@
sp<RpcSession>::fromExisting(this), &version);
status != OK)
return status;
- if (!setProtocolVersion(version)) return BAD_VALUE;
+ if (!setProtocolVersionInternal(version, false)) return BAD_VALUE;
}
// TODO(b/189955605): we should add additional sessions dynamically
@@ -506,11 +519,7 @@
return status;
}
-#ifdef BINDER_RPC_SINGLE_THREADED
- constexpr size_t outgoingThreads = 1;
-#else // BINDER_RPC_SINGLE_THREADED
size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads);
-#endif // BINDER_RPC_SINGLE_THREADED
ALOGI_IF(outgoingThreads != numThreadsAvailable,
"Server hints client to start %zu outgoing threads, but client will only start %zu "
"because it is preconfigured to start at most %zu outgoing threads.",
diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h
index 9d94e00..428e272 100644
--- a/libs/binder/include/binder/RpcSession.h
+++ b/libs/binder/include/binder/RpcSession.h
@@ -207,6 +207,10 @@
friend RpcState;
explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx);
+ // internal version of setProtocolVersion that
+ // optionally skips the mStartedSetup check
+ [[nodiscard]] bool setProtocolVersionInternal(uint32_t version, bool checkStarted);
+
// for 'target', see RpcState::sendDecStrongToTarget
[[nodiscard]] status_t sendDecStrongToTarget(uint64_t address, size_t target);
@@ -344,6 +348,7 @@
RpcMutex mMutex; // for all below
+ bool mStartedSetup = false;
size_t mMaxIncomingThreads = 0;
size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads;
std::optional<uint32_t> mProtocolVersion;
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 9042b3a..c0d4487 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -15,9 +15,10 @@
"libutils",
],
rustlibs: [
- "liblibc",
"libbinder_ndk_sys",
"libdowncast_rs",
+ "liblazy_static",
+ "liblibc",
],
host_supported: true,
vendor_available: true,
@@ -171,9 +172,10 @@
"libbinder_ndk",
],
rustlibs: [
- "liblibc",
"libbinder_ndk_sys",
"libdowncast_rs",
+ "liblazy_static",
+ "liblibc",
],
}
diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs
index 2598ebc..f6b09ed 100644
--- a/libs/binder/rust/src/error.rs
+++ b/libs/binder/rust/src/error.rs
@@ -18,7 +18,7 @@
use crate::sys;
use std::error;
-use std::ffi::CStr;
+use std::ffi::{CStr, CString};
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::result;
@@ -104,6 +104,10 @@
// A thread-local `AStatus` would not be valid.
unsafe impl Send for Status {}
+fn to_cstring<T: AsRef<str>>(message: T) -> Option<CString> {
+ CString::new(message.as_ref()).ok()
+}
+
impl Status {
/// Create a status object representing a successful transaction.
pub fn ok() -> Self {
@@ -146,6 +150,11 @@
Self(ptr)
}
+ /// Creates a status object from a service specific error.
+ pub fn new_service_specific_error_str<T: AsRef<str>>(err: i32, message: Option<T>) -> Status {
+ Self::new_service_specific_error(err, message.and_then(to_cstring).as_deref())
+ }
+
/// Create a status object from an exception code
pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status {
if let Some(message) = message {
@@ -158,6 +167,14 @@
}
}
+ /// Creates a status object from an exception code and message.
+ pub fn new_exception_str<T: AsRef<str>>(
+ exception: ExceptionCode,
+ message: Option<T>,
+ ) -> Status {
+ Self::new_exception(exception, message.and_then(to_cstring).as_deref())
+ }
+
/// Create a status object from a raw `AStatus` pointer.
///
/// # Safety
@@ -371,3 +388,41 @@
self.0
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn make_service_specific_error() {
+ let status = Status::new_service_specific_error_str(-42, Some("message"));
+
+ assert!(!status.is_ok());
+ assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC);
+ assert_eq!(status.service_specific_error(), -42);
+ assert_eq!(
+ status.get_description(),
+ "Status(-8, EX_SERVICE_SPECIFIC): '-42: message'".to_string()
+ );
+ }
+
+ #[test]
+ fn make_exception() {
+ let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("message"));
+
+ assert!(!status.is_ok());
+ assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
+ assert_eq!(status.service_specific_error(), 0);
+ assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): 'message'".to_string());
+ }
+
+ #[test]
+ fn make_exception_null() {
+ let status = Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some("one\0two"));
+
+ assert!(!status.is_ok());
+ assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE);
+ assert_eq!(status.service_specific_error(), 0);
+ assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string());
+ }
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 67872a9..195d9ac 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -111,6 +111,7 @@
pub use error::{ExceptionCode, Status, StatusCode};
pub use native::{
add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
+ LazyServiceGuard,
};
pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
pub use proxy::{
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 9e2cef1..3a6dadd 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -22,6 +22,7 @@
use crate::proxy::SpIBinder;
use crate::sys;
+use lazy_static::lazy_static;
use std::convert::TryFrom;
use std::ffi::{c_void, CStr, CString};
use std::fs::File;
@@ -30,6 +31,7 @@
use std::os::raw::c_char;
use std::os::unix::io::FromRawFd;
use std::slice;
+use std::sync::Mutex;
/// Rust wrapper around Binder remotable objects.
///
@@ -487,6 +489,8 @@
/// If persist is true then shut down will be blocked until this function is called again with
/// persist false. If this is to be the initial state, call this function before calling
/// register_lazy_service.
+///
+/// Consider using [`LazyServiceGuard`] rather than calling this directly.
pub fn force_lazy_services_persist(persist: bool) {
unsafe {
// Safety: No borrowing or transfer of ownership occurs here.
@@ -494,6 +498,59 @@
}
}
+/// An RAII object to ensure a process which registers lazy services is not killed. During the
+/// lifetime of any of these objects the service manager will not not kill the process even if none
+/// of its lazy services are in use.
+#[must_use]
+#[derive(Debug)]
+pub struct LazyServiceGuard {
+ // Prevent construction outside this module.
+ _private: (),
+}
+
+lazy_static! {
+ // Count of how many LazyServiceGuard objects are in existence.
+ static ref GUARD_COUNT: Mutex<u64> = Mutex::new(0);
+}
+
+impl LazyServiceGuard {
+ /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this
+ /// process.
+ pub fn new() -> Self {
+ let mut count = GUARD_COUNT.lock().unwrap();
+ *count += 1;
+ if *count == 1 {
+ // It's important that we make this call with the mutex held, to make sure
+ // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly
+ // sequenced. (That also means we can't just use an AtomicU64.)
+ force_lazy_services_persist(true);
+ }
+ Self { _private: () }
+ }
+}
+
+impl Drop for LazyServiceGuard {
+ fn drop(&mut self) {
+ let mut count = GUARD_COUNT.lock().unwrap();
+ *count -= 1;
+ if *count == 0 {
+ force_lazy_services_persist(false);
+ }
+ }
+}
+
+impl Clone for LazyServiceGuard {
+ fn clone(&self) -> Self {
+ Self::new()
+ }
+}
+
+impl Default for LazyServiceGuard {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
/// Tests often create a base BBinder instance; so allowing the unit
/// type to be remotable translates nicely to Binder::new(()).
impl Remotable for () {
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 862fc2a..254efae 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -133,6 +133,14 @@
}
}
+fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> {
+ if let Some(service) = service {
+ FromIBinder::try_from(service)
+ } else {
+ Err(StatusCode::NAME_NOT_FOUND)
+ }
+}
+
pub mod unstable_api {
use super::{sys, SpIBinder};
@@ -780,21 +788,13 @@
/// Retrieve an existing service for a particular interface, blocking for a few
/// seconds if it doesn't yet exist.
pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
- let service = get_service(name);
- match service {
- Some(service) => FromIBinder::try_from(service),
- None => Err(StatusCode::NAME_NOT_FOUND),
- }
+ interface_cast(get_service(name))
}
/// Retrieve an existing service for a particular interface, or start it if it
/// is configured as a dynamic service and isn't yet started.
pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
- let service = wait_for_service(name);
- match service {
- Some(service) => FromIBinder::try_from(service),
- None => Err(StatusCode::NAME_NOT_FOUND),
- }
+ interface_cast(wait_for_service(name))
}
/// Check if a service is declared (e.g. in a VINTF manifest)
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 6641b77..78731b1 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -161,7 +161,8 @@
],
srcs: [
- ":framework_native_aidl",
+ ":framework_native_aidl_binder",
+ ":framework_native_aidl_gui",
":inputconstants_aidl",
":libgui_bufferqueue_sources",
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 45c958e..31bf895 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -2,9 +2,8 @@
alecmouri@google.com
chaviw@google.com
chrisforbes@google.com
-jessehall@google.com
lpy@google.com
-mathias@google.com
+jreck@google.com
racarr@google.com
vishnun@google.com
diff --git a/libs/sensor/fuzz/sensor_fuzzer/Android.bp b/libs/sensor/fuzz/sensor_fuzzer/Android.bp
new file mode 100644
index 0000000..685c549
--- /dev/null
+++ b/libs/sensor/fuzz/sensor_fuzzer/Android.bp
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ */
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_fuzz {
+ name: "sensor_fuzzer",
+ srcs: [
+ "sensor_fuzzer.cpp",
+ ],
+ shared_libs: [
+ "libsensor",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ "liblog",
+ "libhardware",
+ "libpermission",
+ ],
+ export_shared_lib_headers: [
+ "libbinder",
+ "libpermission",
+ "libhardware",
+ ],
+}
diff --git a/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp b/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp
new file mode 100644
index 0000000..129f430
--- /dev/null
+++ b/libs/sensor/fuzz/sensor_fuzzer/sensor_fuzzer.cpp
@@ -0,0 +1,51 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2022 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <sensor/Sensor.h>
+using namespace android;
+
+const int MAX_STR_LEN = 32;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ struct sensor_t sensor_type;
+ sensor_type.name = fdp.ConsumeBytesAsString(MAX_STR_LEN).c_str();
+ sensor_type.vendor = fdp.ConsumeBytesAsString(MAX_STR_LEN).c_str();
+ sensor_type.stringType = "";
+ sensor_type.requiredPermission = "";
+ sensor_type.version = fdp.ConsumeIntegral<int>();
+ sensor_type.handle = fdp.ConsumeIntegral<int>();
+ sensor_type.type = fdp.ConsumeIntegral<int>();
+ sensor_type.maxRange = fdp.ConsumeFloatingPoint<float>();
+ sensor_type.resolution = fdp.ConsumeFloatingPoint<float>();
+ sensor_type.power = fdp.ConsumeFloatingPoint<float>();
+ sensor_type.minDelay = fdp.ConsumeIntegral<int32_t>();
+ sensor_type.fifoReservedEventCount = fdp.ConsumeIntegral<uint32_t>();
+ sensor_type.fifoMaxEventCount = fdp.ConsumeIntegral<uint32_t>();
+ int halVersion = fdp.ConsumeIntegral<int>();
+ Sensor sensor1(&sensor_type, halVersion);
+ uint8_t buffer[size];
+ for (int i = 0; i < size; i++) buffer[i] = data[i];
+ sensor1.flatten(buffer, size);
+ std::vector<uint8_t> buffer1(sensor1.getFlattenedSize());
+ auto ab = sensor1.unflatten(buffer1.data(), buffer1.size());
+ return 0;
+}
+
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 26c33d2..a92cb94 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -1261,7 +1261,8 @@
if (!identifier.uniqueId.empty()) {
rawDescriptor += "uniqueId:";
rawDescriptor += identifier.uniqueId;
- } else if (identifier.nonce != 0) {
+ }
+ if (identifier.nonce != 0) {
rawDescriptor += StringPrintf("nonce:%04x", identifier.nonce);
}
@@ -1289,16 +1290,20 @@
// of Android. In practice we sometimes get devices that cannot be uniquely
// identified. In this case we enforce uniqueness between connected devices.
// Ideally, we also want the descriptor to be short and relatively opaque.
+ // Note that we explicitly do not use the path or location for external devices
+ // as their path or location will change as they are plugged/unplugged or moved
+ // to different ports. We do fallback to using name and location in the case of
+ // internal devices which are detected by the vendor and product being 0 in
+ // generateDescriptor. If two identical descriptors are detected we will fallback
+ // to using a 'nonce' and incrementing it until the new descriptor no longer has
+ // a match with any existing descriptors.
identifier.nonce = 0;
std::string rawDescriptor = generateDescriptor(identifier);
- if (identifier.uniqueId.empty()) {
- // If it didn't have a unique id check for conflicts and enforce
- // uniqueness if necessary.
- while (getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) {
- identifier.nonce++;
- rawDescriptor = generateDescriptor(identifier);
- }
+ // Enforce that the generated descriptor is unique.
+ while (hasDeviceWithDescriptorLocked(identifier.descriptor)) {
+ identifier.nonce++;
+ rawDescriptor = generateDescriptor(identifier);
}
ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(),
identifier.descriptor.c_str());
@@ -1373,13 +1378,22 @@
return vibrators;
}
-EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const {
- for (const auto& [id, device] : mDevices) {
+/**
+ * Checks both mDevices and mOpeningDevices for a device with the descriptor passed.
+ */
+bool EventHub::hasDeviceWithDescriptorLocked(const std::string& descriptor) const {
+ for (const auto& device : mOpeningDevices) {
if (descriptor == device->identifier.descriptor) {
- return device.get();
+ return true;
}
}
- return nullptr;
+
+ for (const auto& [id, device] : mDevices) {
+ if (descriptor == device->identifier.descriptor) {
+ return true;
+ }
+ }
+ return false;
}
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 5120860..fc398a2 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,6 +38,26 @@
namespace android {
+/**
+ * Determines if the identifiers passed are a sub-devices. Sub-devices are physical devices
+ * that expose multiple input device paths such a keyboard that also has a touchpad input.
+ * These are separate devices with unique descriptors in EventHub, but InputReader should
+ * create a single InputDevice for them.
+ * Sub-devices are detected by the following criteria:
+ * 1. The vendor, product, bus, version, and unique id match
+ * 2. The location matches. The location is used to distinguish a single device with multiple
+ * inputs versus the same device plugged into multiple ports.
+ */
+
+static bool isSubDevice(const InputDeviceIdentifier& identifier1,
+ const InputDeviceIdentifier& identifier2) {
+ return (identifier1.vendor == identifier2.vendor &&
+ identifier1.product == identifier2.product && identifier1.bus == identifier2.bus &&
+ identifier1.version == identifier2.version &&
+ identifier1.uniqueId == identifier2.uniqueId &&
+ identifier1.location == identifier2.location);
+}
+
// --- InputReader ---
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -277,8 +297,9 @@
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
int32_t eventHubId, const InputDeviceIdentifier& identifier) {
auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
- return devicePair.second->getDescriptor().size() && identifier.descriptor.size() &&
- devicePair.second->getDescriptor() == identifier.descriptor;
+ const InputDeviceIdentifier identifier2 =
+ devicePair.second->getDeviceInfo().getIdentifier();
+ return isSubDevice(identifier, identifier2);
});
std::shared_ptr<InputDevice> device;
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 3c3f88e..cf8a841 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -643,7 +643,6 @@
void scanDevicesLocked() REQUIRES(mLock);
status_t readNotifyLocked() REQUIRES(mLock);
- Device* getDeviceByDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock);
Device* getDeviceLocked(int32_t deviceId) const REQUIRES(mLock);
Device* getDeviceByPathLocked(const std::string& devicePath) const REQUIRES(mLock);
/**
@@ -653,6 +652,9 @@
Device* getDeviceByFdLocked(int fd) const REQUIRES(mLock);
int32_t getNextControllerNumberLocked(const std::string& name) REQUIRES(mLock);
+
+ bool hasDeviceWithDescriptorLocked(const std::string& descriptor) const REQUIRES(mLock);
+
void releaseControllerNumberLocked(int32_t num) REQUIRES(mLock);
void reportDeviceAddedForStatisticsLocked(const InputDeviceIdentifier& identifier,
Flags<InputDeviceClass> classes) REQUIRES(mLock);
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index ef68a84..6ef6e44 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -180,6 +180,20 @@
}
/**
+ * Ensure that two identical devices get assigned unique descriptors from EventHub.
+ */
+TEST_F(EventHubTest, DevicesWithMatchingUniqueIdsAreUnique) {
+ std::unique_ptr<UinputHomeKey> keyboard2 = createUinputDevice<UinputHomeKey>();
+ int32_t deviceId2;
+ ASSERT_NO_FATAL_FAILURE(deviceId2 = waitForDeviceCreation());
+
+ ASSERT_NE(mEventHub->getDeviceIdentifier(mDeviceId).descriptor,
+ mEventHub->getDeviceIdentifier(deviceId2).descriptor);
+ keyboard2.reset();
+ waitForDeviceClose(deviceId2);
+}
+
+/**
* Ensure that input_events are generated with monotonic clock.
* That means input_event should receive a timestamp that is in the future of the time
* before the event was sent.