Add event ID generator.

Bug: 144889238
Test: atest libinput_test
Change-Id: I0fd192b3c5a08326aa6a6598025c7b8cdc64cce4
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