Add event ID generator.

Bug: 144889238
Test: atest libinput_test
Change-Id: I0fd192b3c5a08326aa6a6598025c7b8cdc64cce4
diff --git a/include/input/Input.h b/include/input/Input.h
index 14a7288..b86d761 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -266,6 +266,38 @@
 const char* motionClassificationToString(MotionClassification classification);
 
 /**
+ * Generator of unique numbers used to identify input events.
+ *
+ * Layout of ID:
+ *     |--------------------------|---------------------------|
+ *     |   2 bits for source      | 30 bits for random number |
+ *     |--------------------------|---------------------------|
+ */
+class IdGenerator {
+private:
+    static constexpr uint32_t SOURCE_SHIFT = 30;
+
+public:
+    // Used to divide integer space to ensure no conflict among these sources./
+    enum class Source : int32_t {
+        INPUT_READER = 0x0 << SOURCE_SHIFT,
+        INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT,
+        OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events
+    };
+    IdGenerator(Source source);
+
+    int32_t nextId() const;
+
+    // Extract source from given id.
+    static inline Source getSource(int32_t id) { return static_cast<Source>(SOURCE_MASK & id); }
+
+private:
+    const Source mSource;
+
+    static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT;
+};
+
+/**
  * Invalid value for cursor position. Used for non-mouse events, tests and injected events. Don't
  * use it for direct comparison with any other value, because NaN isn't equal to itself according to
  * IEEE 754. Use isnan() instead to check if a cursor position is valid.
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 2a73dc0..abd031a 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -17,7 +17,9 @@
 #define LOG_TAG "Input"
 //#define LOG_NDEBUG 0
 
+#include <cutils/compiler.h>
 #include <limits.h>
+#include <string.h>
 
 #include <input/Input.h>
 #include <input/InputDevice.h>
@@ -25,6 +27,7 @@
 
 #ifdef __ANDROID__
 #include <binder/Parcel.h>
+#include <sys/random.h>
 #endif
 
 namespace android {
@@ -40,6 +43,32 @@
     }
 }
 
+// --- IdGenerator ---
+IdGenerator::IdGenerator(Source source) : mSource(source) {}
+
+int32_t IdGenerator::nextId() const {
+    constexpr uint32_t SEQUENCE_NUMBER_MASK = ~SOURCE_MASK;
+    int32_t id = 0;
+
+// Avoid building against syscall getrandom(2) on host, which will fail build on Mac. Host doesn't
+// use sequence number so just always return mSource.
+#ifdef __ANDROID__
+    constexpr size_t BUF_LEN = sizeof(id);
+    size_t totalBytes = 0;
+    while (totalBytes < BUF_LEN) {
+        ssize_t bytes = TEMP_FAILURE_RETRY(getrandom(&id, BUF_LEN, GRND_NONBLOCK));
+        if (CC_UNLIKELY(bytes < 0)) {
+            ALOGW("Failed to fill in random number for sequence number: %s.", strerror(errno));
+            id = 0;
+            break;
+        }
+        totalBytes += bytes;
+    }
+#endif // __ANDROID__
+
+    return (id & SEQUENCE_NUMBER_MASK) | static_cast<int32_t>(mSource);
+}
+
 // --- InputEvent ---
 
 const char* inputEventTypeToString(int32_t type) {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index fb21d5e..3b57146 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -2,6 +2,7 @@
 cc_test {
     name: "libinput_tests",
     srcs: [
+        "IdGenerator_test.cpp",
         "InputChannel_test.cpp",
         "InputDevice_test.cpp",
         "InputEvent_test.cpp",
diff --git a/libs/input/tests/IdGenerator_test.cpp b/libs/input/tests/IdGenerator_test.cpp
new file mode 100644
index 0000000..f7fc3c0
--- /dev/null
+++ b/libs/input/tests/IdGenerator_test.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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 <gtest/gtest.h>
+#include <input/Input.h>
+#include <ios>
+#include <memory>
+#include <unordered_set>
+
+namespace android::test {
+
+class IdGeneratorTest : public testing::TestWithParam<IdGenerator::Source> {
+protected:
+    void SetUp() override { mGenerator.reset(new IdGenerator(GetParam())); }
+
+    std::unique_ptr<IdGenerator> mGenerator;
+};
+
+TEST_P(IdGeneratorTest, GenerateRandomNumber) {
+    for (int i = 0; i < 500; ++i) {
+        mGenerator->nextId();
+    }
+}
+
+TEST_P(IdGeneratorTest, GenerateRandomNumberWithProperFlag) {
+    for (int i = 0; i < 500; ++i) {
+        int32_t id = mGenerator->nextId();
+        IdGenerator::Source source = IdGenerator::getSource(id);
+        EXPECT_EQ(source, GetParam())
+                << std::hex << "Generator generated a value with wrong source. Value: 0x" << id
+                << " Source: 0x" << static_cast<int32_t>(source);
+    }
+}
+
+INSTANTIATE_TEST_SUITE_P(SourceInstantiation, IdGeneratorTest,
+                         testing::Values(IdGenerator::Source::INPUT_READER,
+                                         IdGenerator::Source::INPUT_DISPATCHER,
+                                         IdGenerator::Source::OTHER));
+} // namespace android::test