* Copyright (c) 2021-2025 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 "multimodal_event_handler.h"
#include <fstream>
#include "cJSON.h"
#include "config_policy_utils.h"
#include "event_log_helper.h"
#ifdef OHOS_BUILD_ENABLE_INPUT_EVENT_HOOK
#include "input_event_hook_handler.h"
#endif
#include "input_manager_impl.h"
#ifdef OHOS_BUILD_ENABLE_KEY_HOOK
#include "key_event_hook_handler.h"
#endif
#include "long_press_event_subscribe_manager.h"
#include "mmi_client.h"
#include "multimodal_input_connect_manager.h"
#include "proto.h"
#include "tablet_event_input_subscribe_manager.h"
#include "pre_monitor_manager.h"
#include "util.h"
#undef MMI_LOG_DOMAIN
#define MMI_LOG_DOMAIN MMI_LOG_HANDLER
#undef MMI_LOG_TAG
#define MMI_LOG_TAG "MultimodalEventHandler"
namespace OHOS {
namespace MMI {
namespace {
constexpr int32_t MIN_MULTI_TOUCH_POINT_NUM { 0 };
constexpr int32_t MAX_MULTI_TOUCH_POINT_NUM { 10 };
constexpr int32_t UNKNOWN_MULTI_TOUCH_POINT_NUM { -1 };
constexpr std::uintmax_t MAX_SIZE_OF_INPUT_PRODUCT_CONFIG { 4096 };
}
void OnConnected(const IfMMIClient& client)
{
CALL_DEBUG_ENTER;
InputMgrImpl.OnConnected();
INPUT_DEVICE_IMPL.OnConnected();
#ifdef OHOS_BUILD_ENABLE_KEYBOARD
KeyEventInputSubscribeMgr.OnConnected();
#endif
#ifdef OHOS_BUILD_ENABLE_SWITCH
SWITCH_EVENT_INPUT_SUBSCRIBE_MGR.OnConnected();
#endif
TABLET_EVENT_INPUT_SUBSCRIBE_MGR.OnConnected();
#ifdef OHOS_BUILD_ENABLE_MONITOR
IMonitorMgr.OnConnected();
PRE_MONITOR_MGR.OnConnected();
#endif
#ifdef OHOS_BUILD_ENABLE_INTERCEPTOR
InputInterMgr->OnConnected();
#endif
INPUT_ACTIVE_SUBSCRIBE_MGR.OnConnected();
DEVICE_CONSUMER.OnConnected();
#ifdef OHOS_BUILD_ENABLE_KEY_HOOK
KEY_EVENT_HOOK_HANDLER.OnConnected();
#endif
#ifdef OHOS_BUILD_ENABLE_INPUT_EVENT_HOOK
INPUT_EVENT_HOOK_HANDLER.OnConnected();
#endif
LONG_PRESS_EVENT_SUBSCRIBE_MGR.OnConnected();
}
void OnDisconnected(const IfMMIClient &client)
{
CALL_DEBUG_ENTER;
InputMgrImpl.OnDisconnected();
INPUT_DEVICE_IMPL.OnDisconnected();
#ifdef OHOS_BUILD_ENABLE_MONITOR
IMonitorMgr.OnDisconnected();
#endif
}
MultimodalEventHandler::MultimodalEventHandler() {}
MultimodalEventHandler::~MultimodalEventHandler()
{
std::lock_guard<std::mutex> guard(mtx_);
if (client_ != nullptr) {
client_->Stop();
client_ = nullptr;
}
MMI_HILOGE("MultimodalEventHandler is deleted.");
}
#ifdef OHOS_BUILD_ENABLE_KEYBOARD
int32_t MultimodalEventHandler::SubscribeKeyEvent(
const KeyEventInputSubscribeManager::SubscribeKeyEventInfo &subscribeInfo)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->SubscribeKeyEvent(subscribeInfo.GetSubscribeId(),
subscribeInfo.GetKeyOption());
}
int32_t MultimodalEventHandler::UnsubscribeKeyEvent(int32_t subscribeId)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->UnsubscribeKeyEvent(subscribeId);
}
int32_t MultimodalEventHandler::SubscribeHotkey(
const KeyEventInputSubscribeManager::SubscribeKeyEventInfo &subscribeInfo)
{
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->SubscribeHotkey(
subscribeInfo.GetSubscribeId(), subscribeInfo.GetKeyOption());
}
int32_t MultimodalEventHandler::UnsubscribeHotkey(int32_t subscribeId)
{
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->UnsubscribeHotkey(subscribeId);
}
int32_t MultimodalEventHandler::InjectEvent(const std::shared_ptr<KeyEvent> keyEvent, bool isNativeInject)
{
CALL_DEBUG_ENTER;
CHKPR(keyEvent, ERROR_NULL_POINTER);
EndLogTraceId(keyEvent->GetId());
LogTracer lt(keyEvent->GetId(), keyEvent->GetEventType(), keyEvent->GetKeyAction());
if (keyEvent->GetKeyCode() < 0) {
MMI_HILOGE("KeyCode is invalid");
return RET_ERR;
}
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->InjectKeyEvent(keyEvent, isNativeInject);
if (ret != 0) {
MMI_HILOGE("Send to server failed, ret:%{public}d", ret);
return RET_ERR;
}
return RET_OK;
}
#endif
#ifdef OHOS_BUILD_ENABLE_KEY_PRESSED_HANDLER
int32_t MultimodalEventHandler::SubscribeKeyMonitor(const KeyMonitorOption &keyOption)
{
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->SubscribeKeyMonitor(keyOption);
}
int32_t MultimodalEventHandler::UnsubscribeKeyMonitor(const KeyMonitorOption &keyOption)
{
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->UnsubscribeKeyMonitor(keyOption);
}
#endif
#ifdef OHOS_BUILD_ENABLE_SWITCH
int32_t MultimodalEventHandler::SubscribeSwitchEvent(int32_t subscribeId, int32_t switchType)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->SubscribeSwitchEvent(subscribeId, switchType);
}
int32_t MultimodalEventHandler::UnsubscribeSwitchEvent(int32_t subscribeId)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->UnsubscribeSwitchEvent(subscribeId);
}
#endif
int32_t MultimodalEventHandler::SubscribeTabletProximity(int32_t subscribeId)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->SubscribeTabletProximity(subscribeId);
}
int32_t MultimodalEventHandler::UnsubscribetabletProximity(int32_t subscribeId)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->UnsubscribetabletProximity(subscribeId);
}
int32_t MultimodalEventHandler::SubscribeLongPressEvent(int32_t subscribeId,
const LongPressRequest &longPressRequest)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->SubscribeLongPressEvent(subscribeId, longPressRequest);
}
int32_t MultimodalEventHandler::UnsubscribeLongPressEvent(int32_t subscribeId)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->UnsubscribeLongPressEvent(subscribeId);
}
bool MultimodalEventHandler::InitClient(EventHandlerPtr eventHandler)
{
CALL_DEBUG_ENTER;
std::lock_guard<std::mutex> guard(mtx_);
if (client_ != nullptr) {
if (eventHandler != nullptr) {
client_->MarkIsEventHandlerChanged(eventHandler);
}
return true;
}
client_ = std::make_shared<MMIClient>();
client_->SetEventHandler(eventHandler);
client_->RegisterConnectedFunction(&OnConnected);
client_->RegisterDisconnectedFunction(&OnDisconnected);
if (!client_->Start()) {
client_ = nullptr;
MMI_HILOGE("The client fails to start");
return false;
}
EventHandlerPtr eventHandlerPtr = client_->GetEventHandler();
CHKPF(eventHandlerPtr);
if (!eventHandlerPtr->PostTask([this] { SetClientInfo(GetPid(), GetThisThreadId()); })) {
MMI_HILOGE("Send reconnect event failed");
return false;
}
return true;
}
MMIClientPtr MultimodalEventHandler::GetMMIClient()
{
std::lock_guard<std::mutex> guard(mtx_);
CHKPP(client_);
return client_->GetSharedPtr();
}
#if defined(OHOS_BUILD_ENABLE_POINTER) || defined(OHOS_BUILD_ENABLE_TOUCH)
int32_t MultimodalEventHandler::InjectPointerEvent(std::shared_ptr<PointerEvent> pointerEvent, bool isNativeInject,
int32_t useCoordinate)
{
CHKPR(pointerEvent, ERROR_NULL_POINTER);
EventLogHelper::PrintEventData(pointerEvent, MMI_LOG_HEADER);
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->InjectPointerEvent(pointerEvent, isNativeInject, useCoordinate);
if (ret != 0) {
MMI_HILOGE("Send to server failed, ret:%{public}d", ret);
return ret;
}
return RET_OK;
}
#endif
#if defined(OHOS_BUILD_ENABLE_POINTER) || defined(OHOS_BUILD_ENABLE_TOUCH)
int32_t MultimodalEventHandler::InjectTouchPadEvent(std::shared_ptr<PointerEvent> pointerEvent,
const TouchpadCDG &touchpadCDG, bool isNativeInject)
{
CHKPR(pointerEvent, ERROR_NULL_POINTER);
EventLogHelper::PrintEventData(pointerEvent, MMI_LOG_HEADER);
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->InjectTouchPadEvent(pointerEvent, touchpadCDG, isNativeInject);
if (ret != 0) {
MMI_HILOGE("Send to server failed, ret:%{public}d", ret);
return RET_ERR;
}
return RET_OK;
}
#endif
#if defined(OHOS_BUILD_ENABLE_POINTER) && defined(OHOS_BUILD_ENABLE_POINTER_DRAWING)
int32_t MultimodalEventHandler::MoveMouseEvent(int32_t offsetX, int32_t offsetY)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->MoveMouseEvent(offsetX, offsetY);
if (ret != 0) {
MMI_HILOGE("Send to server failed, ret:%{public}d", ret);
return RET_ERR;
}
return RET_OK;
}
#endif
int32_t MultimodalEventHandler::Authorize(bool isAuthorize)
{
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->Authorize(isAuthorize);
if (ret != RET_OK) {
MMI_HILOGE("Send to server failed, ret:%{public}d", ret);
return RET_ERR;
}
return RET_OK;
}
int32_t MultimodalEventHandler::CancelInjection()
{
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->CancelInjection();
if (ret != RET_OK) {
MMI_HILOGE("Send to server failed, ret:%{public}d", ret);
return RET_ERR;
}
return RET_OK;
}
int32_t MultimodalEventHandler::SetClientInfo(int32_t pid, uint64_t readThreadId)
{
CALL_DEBUG_ENTER;
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
return MULTIMODAL_INPUT_CONNECT_MGR->SetClientInfo(pid, readThreadId);
}
static void ReadMaxTouchPoints(cJSON *jsonProductCfg, int32_t &maxMultiTouchPointNum)
{
if (!cJSON_IsObject(jsonProductCfg)) {
MMI_HILOGE("Not json format");
return;
}
cJSON *jsonTouchscreen = cJSON_GetObjectItemCaseSensitive(jsonProductCfg, "touchscreen");
if (!cJSON_IsObject(jsonTouchscreen)) {
MMI_HILOGE("The jsonTouchscreen is not object");
return;
}
cJSON *jsonMaxTouchPoints = cJSON_GetObjectItemCaseSensitive(jsonTouchscreen, "MaxTouchPoints");
if (!cJSON_IsNumber(jsonMaxTouchPoints)) {
MMI_HILOGE("The jsonMaxTouchPoints is not number");
return;
}
auto sMaxTouchPoints = std::unique_ptr<char, std::function<void(char *)>>(
cJSON_Print(jsonMaxTouchPoints),
[](char *sMaxTouchPoints) {
if (sMaxTouchPoints != nullptr) {
cJSON_free(sMaxTouchPoints);
}
});
CHKPV(sMaxTouchPoints);
MMI_HILOGI("Config of touchscreen.MaxTouchPoints:%{public}s", sMaxTouchPoints.get());
if (!IsInteger(sMaxTouchPoints.get())) {
MMI_HILOGE("Config of touchscreen.MaxTouchPoints is not integer");
return;
}
auto num = static_cast<int32_t>(cJSON_GetNumberValue(jsonMaxTouchPoints));
if ((num < MIN_MULTI_TOUCH_POINT_NUM) || (num > MAX_MULTI_TOUCH_POINT_NUM)) {
MMI_HILOGW("Invalid config: MaxTouchPoints(%{public}d) is out of range[%{public}d, %{public}d]",
num, MIN_MULTI_TOUCH_POINT_NUM, MAX_MULTI_TOUCH_POINT_NUM);
return;
}
maxMultiTouchPointNum = num;
MMI_HILOGI("touchscreen.MaxTouchPoints:%{public}d", maxMultiTouchPointNum);
}
static void ReadMaxTouchPoints(std::ifstream &ifs, int32_t &maxMultiTouchPointNum)
{
std::string cfg { std::istream_iterator<char>(ifs), std::istream_iterator<char>() };
auto jsonProductCfg = std::unique_ptr<cJSON, std::function<void(cJSON *)>>(
cJSON_Parse(cfg.c_str()),
[](cJSON *productCfg) {
if (productCfg != nullptr) {
cJSON_Delete(productCfg);
}
});
CHKPV(jsonProductCfg);
ReadMaxTouchPoints(jsonProductCfg.get(), maxMultiTouchPointNum);
}
static void ReadMaxTouchPoints(const char *cfgPath, int32_t &maxMultiTouchPointNum)
{
std::error_code ec {};
auto fsize = std::filesystem::file_size(cfgPath, ec);
if (ec || (fsize > MAX_SIZE_OF_INPUT_PRODUCT_CONFIG)) {
MMI_HILOGE("Unexpected size of InputProductConfig");
return;
}
std::ifstream ifs(cfgPath);
if (!ifs.is_open()) {
MMI_HILOGE("Can not open config");
return;
}
ReadMaxTouchPoints(ifs, maxMultiTouchPointNum);
ifs.close();
}
void MultimodalEventHandler::ReadMaxMultiTouchPointNum(int32_t &maxMultiTouchPointNum)
{
maxMultiTouchPointNum = -1;
char cfgName[] { "etc/input/input_product_config.json" };
char buf[MAX_PATH_LEN] {};
char *cfgPath = ::GetOneCfgFile(cfgName, buf, sizeof(buf));
if (cfgPath == nullptr) {
MMI_HILOGE("No input product config was found");
return;
}
auto realPath = std::unique_ptr<char, std::function<void(char *)>>(
::realpath(cfgPath, nullptr),
[](auto arr) {
if (arr != nullptr) {
::free(arr);
}
});
if (realPath == nullptr) {
MMI_HILOGI("No input product config");
return;
}
ReadMaxTouchPoints(realPath.get(), maxMultiTouchPointNum);
}
int32_t MultimodalEventHandler::GetMaxMultiTouchPointNum(int32_t &pointNum)
{
static int32_t maxMultiTouchPointNum { UNKNOWN_MULTI_TOUCH_POINT_NUM };
static std::once_flag flag;
std::call_once(flag, []() {
ReadMaxMultiTouchPointNum(maxMultiTouchPointNum);
});
if (maxMultiTouchPointNum < 0) {
pointNum = UNKNOWN_MULTI_TOUCH_POINT_NUM;
return MMI_ERR_NO_PRODUCT_CONFIG;
}
pointNum = maxMultiTouchPointNum;
return RET_OK;
}
}
}