Fetch country code information from Device sysfs path

HID devices report the country code via sysfs. Need to read it
and pass it and save it as a member variable to InputDevice

In subsequent CLs will use it to auto-detect a layout for PK.
More information in DD: go/pk_auto_layout_detection

Test: atest inputflinger_tests:InputReaderTest
Bug: 242715614
Change-Id: I73ca7518dbee3e563c41024bb3ed41261c8d7846
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 3585392..d51d6a7 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -23,6 +23,8 @@
 #include <unordered_map>
 #include <vector>
 
+#include "android/hardware/input/InputDeviceCountryCode.h"
+
 namespace android {
 
 /*
@@ -210,8 +212,10 @@
     };
 
     void initialize(int32_t id, int32_t generation, int32_t controllerNumber,
-            const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal,
-            bool hasMic);
+                    const InputDeviceIdentifier& identifier, const std::string& alias,
+                    bool isExternal, bool hasMic,
+                    hardware::input::InputDeviceCountryCode countryCode =
+                            hardware::input::InputDeviceCountryCode::INVALID);
 
     inline int32_t getId() const { return mId; }
     inline int32_t getControllerNumber() const { return mControllerNumber; }
@@ -223,6 +227,7 @@
     }
     inline bool isExternal() const { return mIsExternal; }
     inline bool hasMic() const { return mHasMic; }
+    inline hardware::input::InputDeviceCountryCode getCountryCode() const { return mCountryCode; }
     inline uint32_t getSources() const { return mSources; }
 
     const MotionRange* getMotionRange(int32_t axis, uint32_t source) const;
@@ -274,9 +279,11 @@
     std::string mAlias;
     bool mIsExternal;
     bool mHasMic;
+    hardware::input::InputDeviceCountryCode mCountryCode;
     uint32_t mSources;
     int32_t mKeyboardType;
     std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
+
     bool mHasVibrator;
     bool mHasBattery;
     bool mHasButtonUnderPad;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 5030d60..29e02cf 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -26,6 +26,7 @@
 filegroup {
     name: "inputconstants_aidl",
     srcs: [
+        "android/hardware/input/InputDeviceCountryCode.aidl",
         "android/os/IInputConstants.aidl",
         "android/os/InputEventInjectionResult.aidl",
         "android/os/InputEventInjectionSync.aidl",
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index a908969..3fe03c7 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -26,6 +26,7 @@
 #include <input/InputEventLabels.h>
 
 using android::base::StringPrintf;
+using android::hardware::input::InputDeviceCountryCode;
 
 namespace android {
 
@@ -177,6 +178,7 @@
         mAlias(other.mAlias),
         mIsExternal(other.mIsExternal),
         mHasMic(other.mHasMic),
+        mCountryCode(other.mCountryCode),
         mSources(other.mSources),
         mKeyboardType(other.mKeyboardType),
         mKeyCharacterMap(other.mKeyCharacterMap),
@@ -192,8 +194,8 @@
 }
 
 void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber,
-        const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal,
-        bool hasMic) {
+                                 const InputDeviceIdentifier& identifier, const std::string& alias,
+                                 bool isExternal, bool hasMic, InputDeviceCountryCode countryCode) {
     mId = id;
     mGeneration = generation;
     mControllerNumber = controllerNumber;
@@ -201,6 +203,7 @@
     mAlias = alias;
     mIsExternal = isExternal;
     mHasMic = hasMic;
+    mCountryCode = countryCode;
     mSources = 0;
     mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
     mHasVibrator = false;
diff --git a/libs/input/android/hardware/input/InputDeviceCountryCode.aidl b/libs/input/android/hardware/input/InputDeviceCountryCode.aidl
new file mode 100644
index 0000000..6bb1a60
--- /dev/null
+++ b/libs/input/android/hardware/input/InputDeviceCountryCode.aidl
@@ -0,0 +1,212 @@
+/*
+ * 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 android.hardware.input;
+
+/**
+ * Constant for HID country code declared by a HID device. These constants are declared as AIDL to
+ * be used by java and native input code.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum InputDeviceCountryCode {
+    /**
+     * Used as default value where country code is not set in the device HID descriptor
+     */
+    INVALID = -1,
+
+    /**
+     * Used as default value when country code is not supported by the HID device. The HID
+     * descriptor sets "00" as the country code in this case.
+     */
+    NOT_SUPPORTED = 0,
+
+    /**
+     * Arabic
+     */
+    ARABIC = 1,
+
+    /**
+     * Belgian
+     */
+    BELGIAN = 2,
+
+    /**
+     * Canadian (Bilingual)
+     */
+    CANADIAN_BILINGUAL = 3,
+
+    /**
+     * Canadian (French)
+     */
+    CANADIAN_FRENCH = 4,
+
+    /**
+     * Czech Republic
+     */
+    CZECH_REPUBLIC = 5,
+
+    /**
+     * Danish
+     */
+    DANISH = 6,
+
+    /**
+     * Finnish
+     */
+    FINNISH = 7,
+
+    /**
+     * French
+     */
+    FRENCH = 8,
+
+    /**
+     * German
+     */
+    GERMAN = 9,
+
+    /**
+     * Greek
+     */
+    GREEK = 10,
+
+    /**
+     * Hebrew
+     */
+    HEBREW = 11,
+
+    /**
+     * Hungary
+     */
+    HUNGARY = 12,
+
+    /**
+     * International (ISO)
+     */
+    INTERNATIONAL = 13,
+
+    /**
+     * Italian
+     */
+    ITALIAN = 14,
+
+    /**
+     * Japan (Katakana)
+     */
+    JAPAN = 15,
+
+    /**
+     * Korean
+     */
+    KOREAN = 16,
+
+    /**
+     * Latin American
+     */
+    LATIN_AMERICAN = 17,
+
+    /**
+     * Netherlands (Dutch)
+     */
+    DUTCH = 18,
+
+    /**
+     * Norwegian
+     */
+    NORWEGIAN = 19,
+
+    /**
+     * Persian
+     */
+    PERSIAN = 20,
+
+    /**
+     * Poland
+     */
+    POLAND = 21,
+
+    /**
+     * Portuguese
+     */
+    PORTUGUESE = 22,
+
+    /**
+     * Russia
+     */
+    RUSSIA = 23,
+
+    /**
+     * Slovakia
+     */
+    SLOVAKIA = 24,
+
+    /**
+     * Spanish
+     */
+    SPANISH = 25,
+
+    /**
+     * Swedish
+     */
+    SWEDISH = 26,
+
+    /**
+     * Swiss (French)
+     */
+    SWISS_FRENCH = 27,
+
+    /**
+     * Swiss (German)
+     */
+    SWISS_GERMAN = 28,
+
+    /**
+     * Switzerland
+     */
+    SWITZERLAND = 29,
+
+    /**
+     * Taiwan
+     */
+    TAIWAN = 30,
+
+    /**
+     * Turkish_Q
+     */
+    TURKISH_Q = 31,
+
+    /**
+     * UK
+     */
+    UK = 32,
+
+    /**
+     * US
+     */
+    US = 33,
+
+    /**
+     * Yugoslavia
+     */
+    YUGOSLAVIA = 34,
+
+    /**
+     * Turkish_F
+     */
+    TURKISH_F = 35,
+}
\ No newline at end of file
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 06a38c8..336763c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -61,6 +61,7 @@
 #define INDENT3 "      "
 
 using android::base::StringPrintf;
+using android::hardware::input::InputDeviceCountryCode;
 
 namespace android {
 
@@ -302,6 +303,24 @@
 }
 
 /**
+ * Read country code information exposed through the sysfs path.
+ */
+static InputDeviceCountryCode readCountryCodeLocked(const std::filesystem::path& sysfsRootPath) {
+    // Check the sysfs root path
+    int hidCountryCode = static_cast<int>(InputDeviceCountryCode::INVALID);
+    std::string str;
+    if (base::ReadFileToString(sysfsRootPath / "country", &str)) {
+        hidCountryCode = std::stoi(str, nullptr, 16);
+        LOG_ALWAYS_FATAL_IF(hidCountryCode > 35 || hidCountryCode < 0,
+                            "HID country code should be in range [0, 35]. Found country code "
+                            "to be %d",
+                            hidCountryCode);
+    }
+
+    return static_cast<InputDeviceCountryCode>(hidCountryCode);
+}
+
+/**
  * Read information about batteries exposed through the sysfs path.
  */
 static std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> readBatteryConfiguration(
@@ -1238,6 +1257,15 @@
     }
 }
 
+InputDeviceCountryCode EventHub::getCountryCode(int32_t deviceId) const {
+    std::scoped_lock _l(mLock);
+    Device* device = getDeviceLocked(deviceId);
+    if (device == nullptr || !device->associatedDevice) {
+        return InputDeviceCountryCode::INVALID;
+    }
+    return device->associatedDevice->countryCode;
+}
+
 void EventHub::setExcludedDevices(const std::vector<std::string>& devices) {
     std::scoped_lock _l(mLock);
 
@@ -1384,6 +1412,7 @@
 
     return std::make_shared<AssociatedDevice>(
             AssociatedDevice{.sysfsRootPath = path,
+                             .countryCode = readCountryCodeLocked(path),
                              .batteryInfos = readBatteryConfiguration(path),
                              .lightInfos = readLightsConfiguration(path)});
 }
@@ -2553,6 +2582,9 @@
                                  device->keyMap.keyLayoutFile.c_str());
             dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n",
                                  device->keyMap.keyCharacterMapFile.c_str());
+            dump += StringPrintf(INDENT3 "CountryCode: %d\n",
+                                 device->associatedDevice ? device->associatedDevice->countryCode
+                                                          : InputDeviceCountryCode::INVALID);
             dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
                                  device->configurationFile.c_str());
             dump += StringPrintf(INDENT3 "VideoDevice: %s\n",
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 5c9e63e..8eadcdc 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -35,6 +35,8 @@
 #include "SwitchInputMapper.h"
 #include "VibratorInputMapper.h"
 
+using android::hardware::input::InputDeviceCountryCode;
+
 namespace android {
 
 InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
@@ -240,6 +242,7 @@
     mSources = 0;
     mClasses = ftl::Flags<InputDeviceClass>(0);
     mControllerNumber = 0;
+    mCountryCode = InputDeviceCountryCode::INVALID;
 
     for_each_subdevice([this](InputDeviceContext& context) {
         mClasses |= context.getDeviceClasses();
@@ -251,6 +254,16 @@
             }
             mControllerNumber = controllerNumber;
         }
+
+        InputDeviceCountryCode countryCode = context.getCountryCode();
+        if (countryCode != InputDeviceCountryCode::INVALID) {
+            if (mCountryCode != InputDeviceCountryCode::INVALID && mCountryCode != countryCode) {
+                ALOGW("InputDevice::configure(): %s device contains multiple unique country "
+                      "codes",
+                      getName().c_str());
+            }
+            mCountryCode = countryCode;
+        }
     });
 
     mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
@@ -422,7 +435,7 @@
 InputDeviceInfo InputDevice::getDeviceInfo() {
     InputDeviceInfo outDeviceInfo;
     outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
-                             mHasMic);
+                             mHasMic, mCountryCode);
     for_each_mapper(
             [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(&outDeviceInfo); });
 
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 8aec367..dfb98f1 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -43,6 +43,7 @@
 
 #include "TouchVideoDevice.h"
 #include "VibrationElement.h"
+#include "android/hardware/input/InputDeviceCountryCode.h"
 
 struct inotify_event;
 
@@ -301,9 +302,9 @@
             int32_t deviceId, int32_t lightId) const = 0;
     virtual void setLightIntensities(int32_t deviceId, int32_t lightId,
                                      std::unordered_map<LightColor, int32_t> intensities) = 0;
-    /*
-     * Query current input state.
-     */
+    /* Query Country code associated with the input device. */
+    virtual hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const = 0;
+    /* Query current input state. */
     virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const = 0;
     virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0;
     virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
@@ -483,6 +484,8 @@
     void setLightIntensities(int32_t deviceId, int32_t lightId,
                              std::unordered_map<LightColor, int32_t> intensities) override final;
 
+    hardware::input::InputDeviceCountryCode getCountryCode(int32_t deviceId) const override final;
+
     void setExcludedDevices(const std::vector<std::string>& devices) override final;
 
     int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final;
@@ -544,7 +547,7 @@
     struct AssociatedDevice {
         // The sysfs root path of the misc device.
         std::filesystem::path sysfsRootPath;
-
+        hardware::input::InputDeviceCountryCode countryCode;
         std::unordered_map<int32_t /*batteryId*/, RawBatteryInfo> batteryInfos;
         std::unordered_map<int32_t /*lightId*/, RawLightInfo> lightInfos;
     };
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 51872ac..141464f 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -158,6 +158,7 @@
     int32_t mId;
     int32_t mGeneration;
     int32_t mControllerNumber;
+    hardware::input::InputDeviceCountryCode mCountryCode;
     InputDeviceIdentifier mIdentifier;
     std::string mAlias;
     ftl::Flags<InputDeviceClass> mClasses;
@@ -311,6 +312,9 @@
     }
 
     inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); }
+    inline hardware::input::InputDeviceCountryCode getCountryCode() const {
+        return mEventHub->getCountryCode(mId);
+    }
     inline int32_t getScanCodeState(int32_t scanCode) const {
         return mEventHub->getScanCodeState(mId, scanCode);
     }
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 9a0c565..ae98415 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -38,9 +38,12 @@
 #include <gtest/gtest.h>
 #include <gui/constants.h>
 
+#include "android/hardware/input/InputDeviceCountryCode.h"
 #include "input/DisplayViewport.h"
 #include "input/Input.h"
 
+using android::hardware::input::InputDeviceCountryCode;
+
 namespace android {
 
 using namespace ftl::flag_operators;
@@ -455,6 +458,7 @@
         BitArray<MSC_MAX> mscBitmask;
         std::vector<VirtualKeyDefinition> virtualKeys;
         bool enabled;
+        InputDeviceCountryCode countryCode;
 
         status_t enable() {
             enabled = true;
@@ -584,6 +588,11 @@
         device->keyCodeStates.replaceValueFor(keyCode, state);
     }
 
+    void setCountryCode(int32_t deviceId, InputDeviceCountryCode countryCode) {
+        Device* device = getDevice(deviceId);
+        device->countryCode = countryCode;
+    }
+
     void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) {
         Device* device = getDevice(deviceId);
         device->scanCodeStates.replaceValueFor(scanCode, state);
@@ -845,6 +854,14 @@
         return AKEY_STATE_UNKNOWN;
     }
 
+    InputDeviceCountryCode getCountryCode(int32_t deviceId) const override {
+        Device* device = getDevice(deviceId);
+        if (device) {
+            return device->countryCode;
+        }
+        return InputDeviceCountryCode::INVALID;
+    }
+
     int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
         Device* device = getDevice(deviceId);
         if (device) {
@@ -2685,6 +2702,17 @@
     ASSERT_EQ(ftl::Flags<InputDeviceClass>(0), mDevice->getClasses());
 }
 
+TEST_F(InputDeviceTest, CountryCodeCorrectlyMapped) {
+    mFakeEventHub->setCountryCode(EVENTHUB_ID, InputDeviceCountryCode::INTERNATIONAL);
+
+    // Configuration
+    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+    InputReaderConfiguration config;
+    mDevice->configure(ARBITRARY_TIME, &config, 0);
+
+    ASSERT_EQ(InputDeviceCountryCode::INTERNATIONAL, mDevice->getDeviceInfo().getCountryCode());
+}
+
 TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) {
     ASSERT_EQ(mDevice->isEnabled(), false);
 }