Merge "libbinder: allow multiple outgoing threads for single-threaded"
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/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp
index 416e34e..2399acd 100644
--- a/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp
+++ b/libs/cputimeinstate/fuzz/cputimeinstate_fuzzer/Android.bp
@@ -16,6 +16,15 @@
  *
  *****************************************************************************
  */
+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: "cputimeinstate_fuzzer",
     srcs: [
diff --git a/libs/sensor/fuzz/bittube_fuzzer/Android.bp b/libs/sensor/fuzz/bittube_fuzzer/Android.bp
index 5d8f401..7af22cc 100644
--- a/libs/sensor/fuzz/bittube_fuzzer/Android.bp
+++ b/libs/sensor/fuzz/bittube_fuzzer/Android.bp
@@ -16,6 +16,15 @@
  *
  *****************************************************************************
  */
+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: "bittube_fuzzer",
     srcs: [
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.