/* * Copyright (c) 2022-2023 Huawei Device Co., Ltd. * 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 "device.h" #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <cstring> #include <fstream> #include <map> #include <regex> #include <sstream> #include <openssl/sha.h> #include <securec.h> #include "devicestatus_define.h" #include "devicestatus_errors.h" #include "fi_log.h" #include "if_stream_wrap.h" #include "napi_constants.h" #include "utility.h" #undef LOG_TAG #define LOG_TAG "Device" namespace OHOS { namespace Msdp { namespace DeviceStatus { struct Range { size_t start { 0 }; size_t end { 0 }; }; namespace { constexpr int32_t COMMENT_SUBSCRIPT { 0 }; constexpr ssize_t MAX_FILE_SIZE_ALLOWED { 0x5000 }; const struct Range KEY_BLOCKS[] { { KEY_ESC, BTN_MISC }, { KEY_OK, BTN_DPAD_UP }, { KEY_ALS_TOGGLE, BTN_TRIGGER_HAPPY } }; } // namespace Device::Device(int32_t deviceId) : deviceId_(deviceId) {} Device::~Device() { Close(); } int32_t Device::Open() { CALL_DEBUG_ENTER; char buf[PATH_MAX] {}; if (realpath(devPath_.c_str(), buf) == nullptr) { FI_HILOGE("Not real path:%{private}s", devPath_.c_str()); return RET_ERR; } int32_t nRetries { 6 }; for (;;) { Utility::ShowUserAndGroup(); Utility::ShowFileAttributes(buf); fd_ = open(buf, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (fd_ < 0) { FI_HILOGE("Open device \'%{public}s\':%{public}s failed", buf, strerror(errno)); if (nRetries-- > 0) { static constexpr int32_t DEFAULT_WAIT_TIME { 500 }; std::this_thread::sleep_for(std::chrono::milliseconds(DEFAULT_WAIT_TIME)); FI_HILOGI("Retry opening the device \'%{public}s\'", buf); } else { return RET_ERR; } } else { FI_HILOGD("Successful opening \'%{public}s\'", buf); break; } } QueryDeviceInfo(); QuerySupportedEvents(); UpdateCapability(); LoadDeviceConfig(); return RET_OK; } void Device::Close() { CALL_DEBUG_ENTER; if (fd_ >= 0) { if (close(fd_) < 0) { FI_HILOGE("Close fd failed, error:%{public}s, fd_:%{public}d", strerror(errno), fd_); } fd_ = -1; } } void Device::Dispatch(const struct epoll_event &ev) { if ((ev.events & EPOLLIN) == EPOLLIN) { FI_HILOGD("Input data received"); } else if ((ev.events & (EPOLLHUP | EPOLLERR)) != 0) { FI_HILOGE("Epoll hangup, errno:%{public}s", strerror(errno)); } } void Device::QueryDeviceInfo() { CALL_DEBUG_ENTER; char buffer[PATH_MAX] = { 0 }; int32_t rc = ioctl(fd_, EVIOCGNAME(sizeof(buffer) - 1), &buffer); if (rc < 0) { FI_HILOGE("Could not get device name, errno:%{public}s", strerror(errno)); } else { name_.assign(buffer); } struct input_id inputId; rc = ioctl(fd_, EVIOCGID, &inputId); if (rc < 0) { FI_HILOGE("Could not get device input id, errno:%{public}s", strerror(errno)); } else { bus_ = inputId.bustype; product_ = inputId.product; vendor_ = inputId.vendor; version_ = inputId.version; } errno_t ret = memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)); if (ret != EOK) { FI_HILOGE("Call memset_s failed"); return; } rc = ioctl(fd_, EVIOCGPHYS(sizeof(buffer) - 1), &buffer); if (rc < 0) { FI_HILOGE("Could not get location:%{public}s", strerror(errno)); } else { phys_.assign(buffer); } ret = memset_s(buffer, sizeof(buffer), 0, sizeof(buffer)); if (ret != EOK) { FI_HILOGE("Call memset_s failed"); return; } rc = ioctl(fd_, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer); if (rc < 0) { FI_HILOGE("Could not get uniq, errno:%{public}s", strerror(errno)); } else { uniq_.assign(buffer); } } void Device::GetEventMask(const std::string &eventName, uint32_t type, std::size_t arrayLength, uint8_t *whichBitMask) const { int32_t rc = ioctl(fd_, EVIOCGBIT(type, arrayLength), whichBitMask); if (rc < 0) { FI_HILOGE("Could not get %{public}s events mask:%{public}s", eventName.c_str(), strerror(errno)); } } void Device::GetPropMask(const std::string &eventName, std::size_t arrayLength, uint8_t *whichBitMask) const { int32_t rc = ioctl(fd_, EVIOCGPROP(arrayLength), whichBitMask); if (rc < 0) { FI_HILOGE("Could not get %{public}s mask:%{public}s", eventName.c_str(), strerror(errno)); } } void Device::QuerySupportedEvents() { CALL_DEBUG_ENTER; GetEventMask("", 0, sizeof(evBitmask_), evBitmask_); GetEventMask("key", EV_KEY, sizeof(keyBitmask_), keyBitmask_); GetEventMask("abs", EV_ABS, sizeof(absBitmask_), absBitmask_); GetEventMask("rel", EV_REL, sizeof(relBitmask_), relBitmask_); GetPropMask("properties", sizeof(propBitmask_), propBitmask_); } void Device::UpdateCapability() { CALL_DEBUG_ENTER; CheckPointers(); CheckPencilMouse(); CheckKeys(); } bool Device::HasAxesOrButton(size_t start, size_t end, const uint8_t* whichBitMask) const { for (size_t type = start; type < end; ++type) { if (TestBit(type, whichBitMask)) { return true; } } return false; } bool Device::HasJoystickAxesOrButtons() const { if (!TestBit(BTN_JOYSTICK - 1, keyBitmask_)) { if (HasAxesOrButton(BTN_JOYSTICK, BTN_DIGI, keyBitmask_) || // BTN_TRIGGER_HAPPY40 + 1 : loop boundary HasAxesOrButton(BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY40 + 1, keyBitmask_) || HasAxesOrButton(BTN_DPAD_UP, BTN_DPAD_RIGHT + 1, keyBitmask_)) { // BTN_DPAD_RIGHT + 1 : loop boundary return true; } } return HasAxesOrButton(ABS_RX, ABS_PRESSURE, absBitmask_); } bool Device::HasAbsCoord() const { return (HasAbs(ABS_X) && HasAbs(ABS_Y)); } bool Device::HasMtCoord() const { return (HasAbs(ABS_MT_POSITION_X) && HasAbs(ABS_MT_POSITION_Y)); } bool Device::HasRelCoord() const { return (HasRel(REL_X) && HasRel(REL_Y)); } void Device::PrintCapsDevice() const { const std::map<std::size_t, std::string> deviceComparisonTable { { DEVICE_CAP_KEYBOARD, "keyboard" }, { DEVICE_CAP_TOUCH, "touch device" }, { DEVICE_CAP_POINTER, "pointer" }, { DEVICE_CAP_TABLET_TOOL, "tablet tool" }, { DEVICE_CAP_TABLET_PAD, "pad" }, { DEVICE_CAP_GESTURE, "gesture" }, { DEVICE_CAP_SWITCH, "switch" }, { DEVICE_CAP_JOYSTICK, "joystick" } }; for (const auto &[cap, name]: deviceComparisonTable) { if (caps_.test(cap)) { FI_HILOGD("This is %{public}s", name.c_str()); } } } void Device::CheckPointers() { CALL_DEBUG_ENTER; if (HasAbsCoord()) { CheckAbs(); } else { CheckJoystick(); } if (HasMtCoord()) { CheckMt(); } CheckAdditional(); PrintCapsDevice(); } void Device::CheckAbs() { CALL_DEBUG_ENTER; if (HasKey(BTN_STYLUS) || HasKey(BTN_TOOL_PEN)) { caps_.set(DEVICE_CAP_TABLET_TOOL); } else if (HasKey(BTN_TOOL_FINGER) && !HasKey(BTN_TOOL_PEN) && !HasProperty(INPUT_PROP_DIRECT)) { caps_.set(DEVICE_CAP_POINTER); } else if (HasAxesOrButton(BTN_MOUSE, BTN_JOYSTICK, keyBitmask_)) { caps_.set(DEVICE_CAP_POINTER); } else if (HasKey(BTN_TOUCH) || HasProperty(INPUT_PROP_DIRECT)) { caps_.set(DEVICE_CAP_TOUCH); } else if (HasJoystickAxesOrButtons()) { caps_.set(DEVICE_CAP_JOYSTICK); } } void Device::CheckJoystick() { CALL_DEBUG_ENTER; if (HasJoystickAxesOrButtons()) { caps_.set(DEVICE_CAP_JOYSTICK); } } void Device::CheckMt() { CALL_DEBUG_ENTER; if (HasKey(BTN_STYLUS) || HasKey(BTN_TOOL_PEN)) { caps_.set(DEVICE_CAP_TABLET_TOOL); } else if (HasKey(BTN_TOOL_FINGER) && !HasKey(BTN_TOOL_PEN) && !HasProperty(INPUT_PROP_DIRECT)) { caps_.set(DEVICE_CAP_POINTER); } else if (HasKey(BTN_TOUCH) || HasProperty(INPUT_PROP_DIRECT)) { caps_.set(DEVICE_CAP_TOUCH); } } void Device::CheckAdditional() { CALL_DEBUG_ENTER; if (!HasCapability(DEVICE_CAP_TABLET_TOOL) && !HasCapability(DEVICE_CAP_POINTER) && !HasCapability(DEVICE_CAP_JOYSTICK) && HasAxesOrButton(BTN_MOUSE, BTN_JOYSTICK, keyBitmask_) && (HasRelCoord() || !HasAbsCoord())) { caps_.set(DEVICE_CAP_POINTER); } } void Device::CheckPencilMouse() { CALL_DEBUG_ENTER; if (name_ == "M-Pencil Mouse") { caps_.set(DEVICE_CAP_POINTER, 0); } } void Device::CheckKeys() { CALL_DEBUG_ENTER; if (!TestBit(EV_KEY, evBitmask_)) { FI_HILOGD("No EV_KEY capability"); return; } size_t length = sizeof(KEY_BLOCKS) / sizeof(struct Range); for (size_t block { 0U }; block < length; ++block) { for (size_t key = KEY_BLOCKS[block].start; key < KEY_BLOCKS[block].end; ++key) { if (TestBit(key, keyBitmask_)) { FI_HILOGD("Found key:%{public}zx", key); caps_.set(DEVICE_CAP_KEYBOARD); return; } } } } std::string Device::MakeConfigFileName() const { std::ostringstream ss; ss << GetVendor() << "_" << GetProduct() << "_" << GetVersion() << "_" << GetName(); std::string fname { ss.str() }; Utility::RemoveSpace(fname); std::ostringstream sp; sp << "/vendor/etc/keymap/" << fname << ".TOML"; return sp.str(); } int32_t Device::ReadConfigFile(const std::string &filePath) { CALL_DEBUG_ENTER; char realPath[PATH_MAX] = { 0 }; if (realpath(filePath.c_str(), realPath) == nullptr) { FI_HILOGE("Path is error, path is %{private}s", filePath.c_str()); return RET_ERR; } IfStreamWrap cfgFile; cfgFile.ifStream = std::ifstream(filePath); if (!cfgFile.IsOpen()) { FI_HILOGE("Failed to open config file"); return FILE_OPEN_FAIL; } std::string tmp; while (std::getline(cfgFile.ifStream, tmp)) { Utility::RemoveSpace(tmp); size_t pos = tmp.find('#'); if ((pos != tmp.npos) && (pos != COMMENT_SUBSCRIPT)) { FI_HILOGE("File format is error"); return RET_ERR; } if (tmp.empty() || (tmp.front() == '#')) { continue; } pos = tmp.find('='); if (tmp.size() == 0) { FI_HILOGE("Invalid size, pos will overflow"); return RET_ERR; } else if ((pos == (tmp.size() - 1)) || (pos == tmp.npos)) { FI_HILOGE("Find config item error"); return RET_ERR; } std::string configItem = tmp.substr(0, pos); std::string value = tmp.substr(pos + 1); if (ConfigItemSwitch(configItem, value) == RET_ERR) { FI_HILOGE("Configuration item error"); return RET_ERR; } } return RET_OK; } int32_t Device::ConfigItemSwitch(const std::string &configItem, const std::string &value) { CALL_DEBUG_ENTER; const std::string CONFIG_ITEM_KEYBOARD_TYPE { "Key.keyboard.type" }; if (configItem.empty() || value.empty() || !Utility::IsInteger(value)) { FI_HILOGE("Invalid configuration encountered"); return RET_ERR; } if (configItem == CONFIG_ITEM_KEYBOARD_TYPE) { keyboardType_ = static_cast<IDevice::KeyboardType>(stoi(value)); } return RET_OK; } int32_t Device::ReadTomlFile(const std::string &filePath) { CALL_DEBUG_ENTER; char temp[PATH_MAX] {}; if (realpath(filePath.c_str(), temp) == nullptr) { FI_HILOGE("Not real path (\'%{private}s\'):%{public}s", filePath.c_str(), strerror(errno)); return RET_ERR; } FI_HILOGD("Config file path:%{private}s", temp); if (!Utility::DoesFileExist(temp)) { FI_HILOGE("File does not exist:%{public}s", temp); return RET_ERR; } if (Utility::GetFileSize(temp) > MAX_FILE_SIZE_ALLOWED) { FI_HILOGE("File size is out of range"); return RET_ERR; } if (ReadConfigFile(std::string(temp)) != RET_OK) { FI_HILOGE("ReadConfigFile failed"); return RET_ERR; } return RET_OK; } void Device::JudgeKeyboardType() { CALL_DEBUG_ENTER; if (TestBit(KEY_Q, keyBitmask_)) { keyboardType_ = IDevice::KEYBOARD_TYPE_ALPHABETICKEYBOARD; FI_HILOGD("The keyboard type is standard"); } else if (TestBit(KEY_HOME, keyBitmask_) && (GetBus() == BUS_BLUETOOTH)) { keyboardType_ = IDevice::KEYBOARD_TYPE_REMOTECONTROL; FI_HILOGD("The keyboard type is remote control"); } else if (TestBit(KEY_KP1, keyBitmask_)) { keyboardType_ = IDevice::KEYBOARD_TYPE_DIGITALKEYBOARD; FI_HILOGD("The keyboard type is digital keyboard"); } else if (TestBit(KEY_LEFTCTRL, keyBitmask_) && TestBit(KEY_RIGHTCTRL, keyBitmask_) && TestBit(KEY_F20, keyBitmask_)) { keyboardType_ = IDevice::KEYBOARD_TYPE_HANDWRITINGPEN; FI_HILOGD("The keyboard type is handwriting pen"); } else { keyboardType_ = IDevice::KEYBOARD_TYPE_UNKNOWN; FI_HILOGD("Undefined keyboard type"); } } void Device::LoadDeviceConfig() { CALL_DEBUG_ENTER; if (ReadTomlFile(MakeConfigFileName()) != RET_OK) { FI_HILOGE("ReadTomlFile failed"); keyboardType_ = IDevice::KEYBOARD_TYPE_NONE; } if (IsKeyboard()) { if ((keyboardType_ <= IDevice::KEYBOARD_TYPE_NONE) || (keyboardType_ >= IDevice::KEYBOARD_TYPE_MAX)) { JudgeKeyboardType(); } } else { keyboardType_ = IDevice::KEYBOARD_TYPE_NONE; } FI_HILOGD("keyboard type:%{public}d", keyboardType_); } } // namespace DeviceStatus } // namespace Msdp } // namespace OHOS