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.