From be31ca34d56298ae493cc2860a6701dbec3b1d88 Mon Sep 17 00:00:00 2001 From: zhukangbin1 Date: Tue, 18 Mar 2025 09:29:45 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=AF=E6=B5=8B=E8=AF=95=E8=83=BD=E5=8A=9B?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhukangbin1 --- .../include/input_manager_impl.h | 1 + .../event_handler/src/input_manager_impl.cpp | 7 + frameworks/proxy/events/src/input_manager.cpp | 5 + .../innerkits/proxy/include/input_manager.h | 7 + multimodalinput_mini.gni | 1 + .../include/i_multimodal_input_connect.h | 1 + .../multimodal_input_connect_manager.h | 1 + .../include/multimodal_input_connect_proxy.h | 1 + .../include/multimodal_input_connect_stub.h | 1 + .../multimodalinput_ipc_interface_code.h | 1 + .../src/multimodal_input_connect_manager.cpp | 8 + .../src/multimodal_input_connect_proxy.cpp | 23 ++ .../src/multimodal_input_connect_stub.cpp | 23 ++ .../multimodal_input_connect_stub_ex_test.cpp | 5 + service/event_dump/include/event_recorder.h | 69 ++++++ service/event_dump/src/event_recorder.cpp | 198 +++++++++++++++ .../src/event_normalize_handler.cpp | 10 + service/module_loader/include/mmi_service.h | 1 + service/module_loader/src/mmi_service.cpp | 16 ++ tools/inject_event/BUILD.gn | 7 +- tools/inject_event/include/event_replayer.h | 58 +++++ tools/inject_event/src/event_replayer.cpp | 232 ++++++++++++++++++ .../src/input_manager_command.cpp | 57 ++++- 23 files changed, 731 insertions(+), 2 deletions(-) create mode 100644 service/event_dump/include/event_recorder.h create mode 100644 service/event_dump/src/event_recorder.cpp create mode 100644 tools/inject_event/include/event_replayer.h create mode 100644 tools/inject_event/src/event_replayer.cpp diff --git a/frameworks/proxy/event_handler/include/input_manager_impl.h b/frameworks/proxy/event_handler/include/input_manager_impl.h index 82c4182a57..565f312dc0 100644 --- a/frameworks/proxy/event_handler/include/input_manager_impl.h +++ b/frameworks/proxy/event_handler/include/input_manager_impl.h @@ -237,6 +237,7 @@ public: int32_t ConvertToCapiKeyAction(int32_t keyAction); int32_t SetInputDeviceEnabled(int32_t deviceId, bool enable, std::function callback); int32_t ShiftAppPointerEvent(const ShiftWindowParam ¶m, bool autoGenDown); + int32_t SetEventRecorderEnable(bool enable); int32_t CheckKnuckleEvent(float pointX, float pointY, bool &touchType); private: diff --git a/frameworks/proxy/event_handler/src/input_manager_impl.cpp b/frameworks/proxy/event_handler/src/input_manager_impl.cpp index a1091c9336..988044e1cf 100644 --- a/frameworks/proxy/event_handler/src/input_manager_impl.cpp +++ b/frameworks/proxy/event_handler/src/input_manager_impl.cpp @@ -2710,6 +2710,13 @@ int32_t InputManagerImpl::ShiftAppPointerEvent(const ShiftWindowParam ¶m, bo #endif // OHOS_BUILD_ENABLE_POINTER || OHOS_BUILD_ENABLE_TOUCH } +int32_t InputManagerImpl::SetEventRecorderEnable(bool enable) +{ + CALL_INFO_TRACE; + std::lock_guard guard(mtx_); + return MULTIMODAL_INPUT_CONNECT_MGR->SetEventRecorderEnable(enable); +} + int32_t InputManagerImpl::SetCustomCursor(int32_t windowId, CustomCursor cursor, CursorOptions options) { CALL_INFO_TRACE; diff --git a/frameworks/proxy/events/src/input_manager.cpp b/frameworks/proxy/events/src/input_manager.cpp index 7cce40b4f2..1ee8cdad1c 100644 --- a/frameworks/proxy/events/src/input_manager.cpp +++ b/frameworks/proxy/events/src/input_manager.cpp @@ -876,6 +876,11 @@ int32_t InputManager::ShiftAppPointerEvent(const ShiftWindowParam ¶m, bool a return InputMgrImpl.ShiftAppPointerEvent(param, autoGenDown); } +int32_t InputManager::SetEventRecorderEnable(bool enable) +{ + return InputMgrImpl.SetEventRecorderEnable(enable); +} + int32_t InputManager::SetCustomCursor(int32_t windowId, CustomCursor cursor, CursorOptions options) { return InputMgrImpl.SetCustomCursor(windowId, cursor, options); diff --git a/interfaces/native/innerkits/proxy/include/input_manager.h b/interfaces/native/innerkits/proxy/include/input_manager.h index 2a0703ef89..5a72e2b4e5 100644 --- a/interfaces/native/innerkits/proxy/include/input_manager.h +++ b/interfaces/native/innerkits/proxy/include/input_manager.h @@ -1120,6 +1120,13 @@ public: */ int32_t ShiftAppPointerEvent(const ShiftWindowParam ¶m, bool autoGenDown = true); + /** + * @brief Controls the event recording functionality for test use. + * @param enable True to start recording, false to stop recording + * @return True if operation was successful, false otherwise + */ + int32_t SetEventRecorderEnable(bool enable = false); + /** * @brief Sets the custom cursor. You can set whether to adjust the cursor size based on the system settings. * @param windowId Indicates the windowId of the window diff --git a/multimodalinput_mini.gni b/multimodalinput_mini.gni index 3b6d8c73d6..67ffb98e30 100644 --- a/multimodalinput_mini.gni +++ b/multimodalinput_mini.gni @@ -120,6 +120,7 @@ declare_args() { "dfx/src/dfx_hisysevent_device.cpp", "event_dispatch/src/event_dispatch_handler.cpp", "event_dump/src/event_dump.cpp", + "event_dump/src/event_recorder.cpp", "event_dump/src/event_statistic.cpp", "event_handler/src/anr_manager.cpp", "event_handler/src/event_normalize_handler.cpp", diff --git a/service/connect_manager/include/i_multimodal_input_connect.h b/service/connect_manager/include/i_multimodal_input_connect.h index 053eb7e662..c50812a1ae 100644 --- a/service/connect_manager/include/i_multimodal_input_connect.h +++ b/service/connect_manager/include/i_multimodal_input_connect.h @@ -181,6 +181,7 @@ public: virtual int32_t GetAllSystemHotkeys(std::vector> &keyOptions) = 0; virtual int32_t SetInputDeviceEnabled(int32_t deviceId, bool enable, int32_t index) = 0; virtual int32_t ShiftAppPointerEvent(const ShiftWindowParam ¶m, bool autoGenDown) = 0; + virtual int32_t SetEventRecorderEnable(bool enable) = 0; virtual int32_t SetMultiWindowScreenId(uint64_t screenId, uint64_t displayNodeScreenId) = 0; }; } // namespace MMI diff --git a/service/connect_manager/include/multimodal_input_connect_manager.h b/service/connect_manager/include/multimodal_input_connect_manager.h index a71ec3b920..f41d45f676 100644 --- a/service/connect_manager/include/multimodal_input_connect_manager.h +++ b/service/connect_manager/include/multimodal_input_connect_manager.h @@ -169,6 +169,7 @@ public: int32_t GetAllSystemHotkeys(std::vector> &keyOptions); int32_t SetInputDeviceEnabled(int32_t deviceId, bool enable, int32_t index); int32_t ShiftAppPointerEvent(const ShiftWindowParam ¶m, bool autoGenDown); + int32_t SetEventRecorderEnable(bool enable); int32_t SetMultiWindowScreenId(uint64_t screenId, uint64_t displayNodeScreenId); private: diff --git a/service/connect_manager/include/multimodal_input_connect_proxy.h b/service/connect_manager/include/multimodal_input_connect_proxy.h index 285bbdd588..7f92a27602 100644 --- a/service/connect_manager/include/multimodal_input_connect_proxy.h +++ b/service/connect_manager/include/multimodal_input_connect_proxy.h @@ -167,6 +167,7 @@ public: int32_t GetAllSystemHotkeys(std::vector> &keyOptions) override; int32_t SetInputDeviceEnabled(int32_t deviceId, bool enable, int32_t index) override; int32_t ShiftAppPointerEvent(const ShiftWindowParam ¶m, bool autoGenDown) override; + int32_t SetEventRecorderEnable(bool enable) override; int32_t SetMultiWindowScreenId(uint64_t screenId, uint64_t displayNodeScreenId) override; private: diff --git a/service/connect_manager/include/multimodal_input_connect_stub.h b/service/connect_manager/include/multimodal_input_connect_stub.h index f93db14fac..9da1eab14b 100644 --- a/service/connect_manager/include/multimodal_input_connect_stub.h +++ b/service/connect_manager/include/multimodal_input_connect_stub.h @@ -180,6 +180,7 @@ protected: int32_t ParseAddInputHandlerData(MessageParcel& data, ParseData& parseData); int32_t StubSetInputDeviceInputEnable(MessageParcel& data, MessageParcel& reply); int32_t StubShiftAppPointerEvent(MessageParcel& data, MessageParcel& reply); + int32_t StubSetEventRecorderEnable(MessageParcel& data, MessageParcel& reply); int32_t StubSetCustomMouseCursor(MessageParcel& data, MessageParcel& reply); int32_t StubSetMultiWindowScreenId(MessageParcel& data, MessageParcel& reply); diff --git a/service/connect_manager/include/multimodalinput_ipc_interface_code.h b/service/connect_manager/include/multimodalinput_ipc_interface_code.h index b649a8b300..63fa25c70a 100644 --- a/service/connect_manager/include/multimodalinput_ipc_interface_code.h +++ b/service/connect_manager/include/multimodalinput_ipc_interface_code.h @@ -133,6 +133,7 @@ enum class MultimodalinputConnectInterfaceCode { SET_INPUT_DEVICE_ENABLE = 109, SHIFT_APP_POINTER_EVENT = 110, + SET_EVENT_RECORDER_ENABLE = 111, SET_CUSTOM_MOUSE_CURSOR = 120, INJECT_TOUCHPAD_EVENT = 121, GET_TOUCHPAD_OPTION = 122, diff --git a/service/connect_manager/src/multimodal_input_connect_manager.cpp b/service/connect_manager/src/multimodal_input_connect_manager.cpp index 4ee7cb63d7..d02a23935f 100644 --- a/service/connect_manager/src/multimodal_input_connect_manager.cpp +++ b/service/connect_manager/src/multimodal_input_connect_manager.cpp @@ -1037,6 +1037,14 @@ int32_t MultimodalInputConnectManager::ShiftAppPointerEvent(const ShiftWindowPar return multimodalInputConnectService_->ShiftAppPointerEvent(param, autoGenDown); } +int32_t MultimodalInputConnectManager::SetEventRecorderEnable(bool enable) +{ + CALL_INFO_TRACE; + std::lock_guard guard(lock_); + CHKPR(multimodalInputConnectService_, INVALID_HANDLER_ID); + return multimodalInputConnectService_->SetEventRecorderEnable(enable); +} + int32_t MultimodalInputConnectManager::SetCustomCursor(int32_t windowId, CustomCursor cursor, CursorOptions options) { std::lock_guard guard(lock_); diff --git a/service/connect_manager/src/multimodal_input_connect_proxy.cpp b/service/connect_manager/src/multimodal_input_connect_proxy.cpp index d9dff483b8..e4b07dc550 100644 --- a/service/connect_manager/src/multimodal_input_connect_proxy.cpp +++ b/service/connect_manager/src/multimodal_input_connect_proxy.cpp @@ -2920,6 +2920,29 @@ int32_t MultimodalInputConnectProxy::ShiftAppPointerEvent(const ShiftWindowParam return RET_OK; } +int32_t MultimodalInputConnectProxy::SetEventRecorderEnable(bool enable) +{ + CALL_DEBUG_ENTER; + MessageParcel data; + if (!data.WriteInterfaceToken(MultimodalInputConnectProxy::GetDescriptor())) { + MMI_HILOGE("Failed to write descriptor"); + return ERR_INVALID_VALUE; + } + WRITEBOOL(data, enable, ERR_INVALID_VALUE); + MessageParcel reply; + MessageOption option; + sptr remote = Remote(); + CHKPR(remote, RET_ERR); + int32_t ret = remote->SendRequest( + static_cast(MultimodalinputConnectInterfaceCode::SET_EVENT_RECORDER_ENABLE), + data, reply, option); + if (ret != RET_OK) { + MMI_HILOGE("MultimodalInputConnectProxy::SetEventRecorderEnable Send request fail, ret:%{public}d", ret); + return ret; + } + return RET_OK; +} + int32_t MultimodalInputConnectProxy::SetCustomCursor(int32_t windowId, CustomCursor cursor, CursorOptions options) __attribute__((no_sanitize("cfi"))) { diff --git a/service/connect_manager/src/multimodal_input_connect_stub.cpp b/service/connect_manager/src/multimodal_input_connect_stub.cpp index 6a1dea49cd..1acc2b9892 100644 --- a/service/connect_manager/src/multimodal_input_connect_stub.cpp +++ b/service/connect_manager/src/multimodal_input_connect_stub.cpp @@ -265,6 +265,9 @@ int32_t MultimodalInputConnectStub::OnRemoteRequest(uint32_t code, MessageParcel case static_cast(MultimodalinputConnectInterfaceCode::SHIFT_APP_POINTER_EVENT): ret = StubShiftAppPointerEvent(data, reply); break; + case static_cast(MultimodalinputConnectInterfaceCode::SET_EVENT_RECORDER_ENABLE): + ret = StubSetEventRecorderEnable(data, reply); + break; case static_cast(MultimodalinputConnectInterfaceCode::INJECT_KEY_EVENT): ret = StubInjectKeyEvent(data, reply); break; @@ -3398,6 +3401,26 @@ int32_t MultimodalInputConnectStub::StubShiftAppPointerEvent(MessageParcel& data return ret; } +int32_t MultimodalInputConnectStub::StubSetEventRecorderEnable(MessageParcel& data, MessageParcel& reply) +{ + CALL_DEBUG_ENTER; + if (!PER_HELPER->VerifySystemApp()) { + MMI_HILOGE("Verify system APP failed"); + return ERROR_NOT_SYSAPI; + } + if (!IsRunning()) { + MMI_HILOGE("Service is not running"); + return MMISERVICE_NOT_RUNNING; + } + bool enable = false; + READBOOL(data, enable, ERR_INVALID_VALUE); + int32_t ret = SetEventRecorderEnable(enable); + if (ret != RET_OK) { + MMI_HILOGE("shift AppPointerEvent failed, ret:%{public}d", ret); + } + return ret; +} + int32_t MultimodalInputConnectStub::StubSetCustomMouseCursor(MessageParcel& data, MessageParcel& reply) { CALL_DEBUG_ENTER; diff --git a/service/connect_manager/test/multimodal_input_connect_stub_ex_test.cpp b/service/connect_manager/test/multimodal_input_connect_stub_ex_test.cpp index 15d60ae96a..8c2d7d8c8a 100644 --- a/service/connect_manager/test/multimodal_input_connect_stub_ex_test.cpp +++ b/service/connect_manager/test/multimodal_input_connect_stub_ex_test.cpp @@ -360,6 +360,11 @@ public: return static_cast(autoGenDown); } + int32_t SetEventRecorderEnable(bool enable) override + { + return static_cast(enable); + } + int32_t InjectTouchPadEvent(std::shared_ptr pointerEvent, const TouchpadCDG &touchpadCDG, bool isNativeInject) override { diff --git a/service/event_dump/include/event_recorder.h b/service/event_dump/include/event_recorder.h new file mode 100644 index 0000000000..de1226cd49 --- /dev/null +++ b/service/event_dump/include/event_recorder.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021-2024 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. + */ + +#ifndef EVENT_RECORDER_H +#define EVENT_RECORDER_H + +#include +#include +#include +#include +#include + +#include "pointer_event.h" +#include "key_event.h" + +namespace OHOS { +namespace MMI { + +class EventRecorder final { +public: + DISALLOW_MOVE(EventRecorder); + EventRecorder(); + ~EventRecorder() = default; + static EventRecorder* GetInstance(); + static constexpr int64_t DEFAULT_RECORDING_DURATION_MS = 10 * 60 * 1000; + int32_t SetEventRecorderEnable(bool enable); + bool RecordEvent(const std::shared_ptr pointerEvent); + bool RecordEvent(const std::shared_ptr keyEvent); + +private: + struct FileHeader { + uint32_t magicNumber; + uint16_t version; + int32_t eventCount; + uint32_t reserved; + }; + bool StartRecording(); + bool StopRecording(); + bool HasRecordingTimedOut(); + bool WriteEvent(std::ofstream& out, std::shared_ptr pointerEvent); + bool WriteEvent(std::ofstream& out, std::shared_ptr keyEvent); + bool CheckRecordEvent(); + bool WriteEventToFile(std::ofstream& out, int32_t eventType, Parcel& parcel); + + std::atomic isRecording_ { false }; + std::ofstream recordFile_; + mutable std::mutex mutex_; + std::chrono::steady_clock::time_point recordStartTime_; + int64_t currentRecordingDurationMs_ { DEFAULT_RECORDING_DURATION_MS }; + std::atomic eventCount_; +}; + +#define EVENT_RECORDER ::OHOS::MMI::EventRecorder::GetInstance() +} // namespace MMI +} // namespace OHOS + +#endif // EVENT_RECORDER_H \ No newline at end of file diff --git a/service/event_dump/src/event_recorder.cpp b/service/event_dump/src/event_recorder.cpp new file mode 100644 index 0000000000..7b4bba5ec6 --- /dev/null +++ b/service/event_dump/src/event_recorder.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (c) 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 "event_recorder.h" + +#include "util_ex.h" + +#undef MMI_LOG_DOMAIN +#define MMI_LOG_DOMAIN MMI_LOG_SERVER +#undef MMI_LOG_TAG +#define MMI_LOG_TAG "EventRecorder" + +namespace OHOS { +namespace MMI { +namespace { +const char* EVENT_FILE_NAME = "/data/service/el1/public/multimodalinput/event.tmp"; +static constexpr uint32_t EVENT_COUNT_OFF_SET = 8; +static constexpr uint32_t MAGIC_NUMBER = 0x4D4D4952; // "MMIR" in ASCII +static constexpr uint16_t FILE_VERSION = 0x0001; +} + +EventRecorder::EventRecorder() : isRecording_(false), + currentRecordingDurationMs_(DEFAULT_RECORDING_DURATION_MS), eventCount_(-1) +{ +} + +EventRecorder* EventRecorder::GetInstance() +{ + static EventRecorder instance_; + return &instance_; +} + +int32_t EventRecorder::SetEventRecorderEnable(bool enable) +{ + bool result = false; + if (enable) { + result = StartRecording(); + } else { + result = StopRecording(); + } + if (result) { + return RET_OK; + } + return RET_ERR; +} + +bool EventRecorder::StartRecording() +{ + CALL_DEBUG_ENTER; + std::lock_guard lock(mutex_); + if (isRecording_) { + MMI_HILOGI("Stopping existing recording to start a new one"); + recordFile_.close(); + isRecording_ = false; + } + recordFile_.open(EVENT_FILE_NAME, std::ios::binary); + if (!recordFile_.is_open()) { + MMI_HILOGE("Failed to open file: %{public}s", EVENT_FILE_NAME); + return false; + } + FileHeader header{ + header.magicNumber = MAGIC_NUMBER, + header.version = FILE_VERSION, + header.eventCount = -1, + header.reserved = 0, + }; + recordFile_.write(reinterpret_cast(&header), sizeof(header)); + if (!recordFile_.good()) { + recordFile_.close(); + return false; + } + isRecording_ = true; + eventCount_ = 0; + recordStartTime_ = std::chrono::steady_clock::now(); + MMI_HILOGI("Recording started to file: %{public}s with duration: %{public}lld ms", + EVENT_FILE_NAME, static_cast(currentRecordingDurationMs_)); + return true; +} + +bool EventRecorder::StopRecording() +{ + CALL_DEBUG_ENTER; + std::lock_guard lock(mutex_); + if (!isRecording_) { + MMI_HILOGE("No recording in progress"); + return false; + } + recordFile_.seekp(EVENT_COUNT_OFF_SET); + if (!recordFile_.good()) { + MMI_HILOGE("Write event count failed!"); + return false; + } + int32_t eventCount = eventCount_.load(); + recordFile_.write(reinterpret_cast(&eventCount), sizeof(eventCount)); + recordFile_.close(); + isRecording_ = false; + MMI_HILOGI("Recording stopped"); + return true; +} + +bool EventRecorder::CheckRecordEvent() +{ + if (!isRecording_ || !recordFile_.is_open()) { + return false; + } + if (HasRecordingTimedOut()) { + MMI_HILOGI("Recording has exceeded maximum duration (%{public}lld ms), stopping automatically", + static_cast(currentRecordingDurationMs_)); + recordFile_.close(); + isRecording_ = false; + return false; + } + return true; +} + +bool EventRecorder::RecordEvent(const std::shared_ptr pointerEvent) +{ + if (!isRecording_) { + return false; + } + std::lock_guard lock(mutex_); + if (!CheckRecordEvent()) { + return false; + } + return WriteEvent(recordFile_, pointerEvent); +} + +bool EventRecorder::RecordEvent(const std::shared_ptr keyEvent) +{ + if (!isRecording_) { + return false; + } + std::lock_guard lock(mutex_); + if (!CheckRecordEvent()) { + return false; + } + return WriteEvent(recordFile_, keyEvent); +} + +bool EventRecorder::HasRecordingTimedOut() +{ + auto now = std::chrono::steady_clock::now(); + auto elapsedMs = std::chrono::duration_cast(now - recordStartTime_).count(); + return elapsedMs >= currentRecordingDurationMs_; +} + +bool EventRecorder::WriteEventToFile(std::ofstream&out, int32_t eventType, Parcel& parcel) +{ + uint32_t dataSize = static_cast(parcel.GetDataSize()); + out.write(reinterpret_cast(&eventType), sizeof(eventType)); + if (!out.good()) { + return false; + } + out.write(reinterpret_cast(&dataSize), sizeof(dataSize)); + if (!out.good()) { + return false; + } + out.write(reinterpret_cast(parcel.GetData()), parcel.GetDataSize()); + if (!out.good()) { + return false; + } + eventCount_++; + return true; +} + +bool EventRecorder::WriteEvent(std::ofstream& out, std::shared_ptr pointerEvent) +{ + Parcel parcel; + int32_t eventType = pointerEvent->GetEventType(); + if (!pointerEvent->WriteToParcel(parcel)) { + return false; + } + return WriteEventToFile(out, eventType, parcel); +} + +bool EventRecorder::WriteEvent(std::ofstream& out, std::shared_ptr keyEvent) +{ + Parcel parcel; + int32_t eventType = keyEvent->GetEventType(); + if (!keyEvent->WriteToParcel(parcel)) { + return false; + } + return WriteEventToFile(out, eventType, parcel); +} +} // namespace MMI +} // namespace OHOS \ No newline at end of file diff --git a/service/event_handler/src/event_normalize_handler.cpp b/service/event_handler/src/event_normalize_handler.cpp index 403e2cd8c9..8ce87f7cb7 100644 --- a/service/event_handler/src/event_normalize_handler.cpp +++ b/service/event_handler/src/event_normalize_handler.cpp @@ -41,6 +41,7 @@ #include "time_cost_chk.h" #include "timer_manager.h" #include "touch_event_normalize.h" +#include "event_recorder.h" #ifdef OHOS_BUILD_ENABLE_POINTER #include "touchpad_transform_processor.h" #include "touchpad_settings_handler.h" @@ -408,6 +409,7 @@ int32_t EventNormalizeHandler::HandleKeyboardEvent(libinput_event* event) } auto packageResult = KeyEventHdr->Normalize(event, keyEvent); EventStatistic::PushEvent(keyEvent); + EVENT_RECORDER->RecordEvent(keyEvent); LogTracer lt(keyEvent->GetId(), keyEvent->GetEventType(), keyEvent->GetKeyAction()); if (packageResult == MULTIDEVICE_SAME_EVENT_MARK) { MMI_HILOGD("The same event reported by multi_device should be discarded"); @@ -515,6 +517,7 @@ int32_t EventNormalizeHandler::HandleMouseEvent(libinput_event* event) auto buttonId = pointerEvent->GetButtonId(); g_buttonPressed = pointerEvent->IsButtonPressed(buttonId); EventStatistic::PushPointerEvent(pointerEvent); + EVENT_RECORDER->RecordEvent(pointerEvent); PointerEvent::PointerItem item; pointerEvent->GetPointerItem(pointerEvent->GetPointerId(), item); if (!item.IsCanceled()) { @@ -576,6 +579,7 @@ int32_t EventNormalizeHandler::HandleTouchPadEvent(libinput_event* event) CHKPR(pointerEvent, ERROR_NULL_POINTER); LogTracer lt(pointerEvent->GetId(), pointerEvent->GetEventType(), pointerEvent->GetPointerAction()); EventStatistic::PushPointerEvent(pointerEvent); + EVENT_RECORDER->RecordEvent(pointerEvent); if (HandleTouchPadTripleTapEvent(pointerEvent)) { return RET_OK; } @@ -622,6 +626,7 @@ int32_t EventNormalizeHandler::HandleGestureEvent(libinput_event* event) LogTracer lt(pointerEvent->GetId(), pointerEvent->GetEventType(), pointerEvent->GetPointerAction()); PointerEventSetPressedKeys(pointerEvent); EventStatistic::PushPointerEvent(pointerEvent); + EVENT_RECORDER->RecordEvent(pointerEvent); nextHandler_->HandlePointerEvent(pointerEvent); auto type = libinput_event_get_type(event); if (type == LIBINPUT_EVENT_GESTURE_SWIPE_END || type == LIBINPUT_EVENT_GESTURE_PINCH_END) { @@ -701,6 +706,7 @@ int32_t EventNormalizeHandler::HandleTouchEvent(libinput_event* event, int64_t f } BytraceAdapter::StopPackageEvent(); EventStatistic::PushPointerEvent(pointerEvent); + EVENT_RECORDER->RecordEvent(pointerEvent); PointerEventSetPressedKeys(pointerEvent); BytraceAdapter::StartBytrace(pointerEvent, BytraceAdapter::TRACE_START); @@ -778,6 +784,7 @@ int32_t EventNormalizeHandler::HandleTableToolEvent(libinput_event* event) LogTracer lt(pointerEvent->GetId(), pointerEvent->GetEventType(), pointerEvent->GetPointerAction()); BytraceAdapter::StartBytrace(pointerEvent, BytraceAdapter::TRACE_START); EventStatistic::PushPointerEvent(pointerEvent); + EVENT_RECORDER->RecordEvent(pointerEvent); nextHandler_->HandleTouchEvent(pointerEvent); if (pointerEvent->GetPointerAction() == PointerEvent::POINTER_ACTION_UP) { pointerEvent->Reset(); @@ -800,6 +807,7 @@ int32_t EventNormalizeHandler::HandleJoystickButtonEvent(libinput_event *event) CHKPR(keyEvent, ERROR_NULL_POINTER); BytraceAdapter::StartBytrace(keyEvent); EventStatistic::PushEvent(keyEvent); + EVENT_RECORDER->RecordEvent(keyEvent); #ifdef OHOS_BUILD_ENABLE_KEYBOARD nextHandler_->HandleKeyEvent(keyEvent); #endif // OHOS_BUILD_ENABLE_KEYBOARD @@ -817,12 +825,14 @@ int32_t EventNormalizeHandler::HandleJoystickAxisEvent(libinput_event *event) PointerEventSetPressedKeys(pointerEvent); BytraceAdapter::StartBytrace(pointerEvent, BytraceAdapter::TRACE_START); EventStatistic::PushPointerEvent(pointerEvent); + EVENT_RECORDER->RecordEvent(pointerEvent); #ifdef OHOS_BUILD_ENABLE_POINTER nextHandler_->HandlePointerEvent(pointerEvent); #endif // OHOS_BUILD_ENABLE_POINTER joystick_.CheckIntention(pointerEvent, [this](std::shared_ptr keyEvent) { BytraceAdapter::StartBytrace(keyEvent); EventStatistic::PushEvent(keyEvent); + EVENT_RECORDER->RecordEvent(keyEvent); #ifdef OHOS_BUILD_ENABLE_KEYBOARD nextHandler_->HandleKeyEvent(keyEvent); #endif // OHOS_BUILD_ENABLE_KEYBOARD diff --git a/service/module_loader/include/mmi_service.h b/service/module_loader/include/mmi_service.h index dca20c3529..e71d92c62f 100644 --- a/service/module_loader/include/mmi_service.h +++ b/service/module_loader/include/mmi_service.h @@ -197,6 +197,7 @@ public: int32_t GetAllSystemHotkeys(std::vector> &keyOptions) override; int32_t SetInputDeviceEnabled(int32_t deviceId, bool enable, int32_t index) override; int32_t ShiftAppPointerEvent(const ShiftWindowParam ¶m, bool autoGenDown) override; + int32_t SetEventRecorderEnable(bool enable) override; int32_t SetMultiWindowScreenId(uint64_t screenId, uint64_t displayNodeScreenId) override; protected: diff --git a/service/module_loader/src/mmi_service.cpp b/service/module_loader/src/mmi_service.cpp index 8758013790..ea20fa9656 100644 --- a/service/module_loader/src/mmi_service.cpp +++ b/service/module_loader/src/mmi_service.cpp @@ -77,6 +77,7 @@ #ifdef PLAYER_FRAMEWORK_EXISTS #include "input_screen_capture_agent.h" #endif // PLAYER_FRAMEWORK_EXISTS +#include "event_recorder.h" #include "tablet_subscriber_handler.h" #undef MMI_LOG_TAG #define MMI_LOG_TAG "MMIService" @@ -3673,6 +3674,21 @@ int32_t MMIService::ShiftAppPointerEvent(const ShiftWindowParam ¶m, bool aut return RET_OK; } +int32_t MMIService::SetEventRecorderEnable(bool enable) +{ + CALL_DEBUG_ENTER; + int32_t ret = delegateTasks_.PostSyncTask( + [enable]() { + return EVENT_RECORDER->SetEventRecorderEnable(enable); + } + ); + if (ret != RET_OK) { + MMI_HILOGE("SetEventRecorderEnable failed, return:%{public}d", ret); + return ret; + } + return RET_OK; +} + int32_t MMIService::SetCustomCursor(int32_t windowId, CustomCursor cursor, CursorOptions options) { CALL_INFO_TRACE; diff --git a/tools/inject_event/BUILD.gn b/tools/inject_event/BUILD.gn index e1747f5e30..a89436a92d 100644 --- a/tools/inject_event/BUILD.gn +++ b/tools/inject_event/BUILD.gn @@ -16,7 +16,10 @@ import("//build/test.gni") import("../../multimodalinput_mini.gni") ohos_source_set("input-manager") { - sources = [ "src/input_manager_command.cpp" ] + sources = [ + "src/event_replayer.cpp", + "src/input_manager_command.cpp", + ] branch_protector_ret = "pac_ret" sanitize = { cfi = true @@ -30,6 +33,8 @@ ohos_source_set("input-manager") { "${mmi_path}/frameworks/proxy/event_handler/include", "${mmi_path}/frameworks/proxy/module_loader/include", "${mmi_path}/service/event_handler/include", + "${mmi_path}/service/event_dump/include", + "${mmi_path}/util/common/include", ] deps = [ diff --git a/tools/inject_event/include/event_replayer.h b/tools/inject_event/include/event_replayer.h new file mode 100644 index 0000000000..a43eb82d29 --- /dev/null +++ b/tools/inject_event/include/event_replayer.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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. + */ + +#ifndef EVENT_REPLAYER_H +#define EVENT_REPLAYER_H + +#include +#include +#include +#include + +#include "key_event.h" +#include "pointer_event.h" + +namespace OHOS { +namespace MMI { +class EventReplayer final { +public: + DISALLOW_MOVE(EventReplayer); + EventReplayer(); + ~EventReplayer(); + bool ReplayFile(const std::string& filePath); + +private: + struct FileHeader { + uint32_t magicNumber; + uint16_t version; + int32_t eventCount; + uint32_t reserved; + }; + void Stop(); + bool ReadFileHeader(std::ifstream& eventFile, FileHeader& header); + bool ReplayEvents(std::ifstream& eventFile, const FileHeader& header); + bool ReadEvent(std::ifstream& in, std::shared_ptr& eventPtr); + bool InjectEvent(int32_t eventType); + bool InitializeEventObjects(); + bool ShouldWait(int64_t currentEventTime, int64_t lastEventTime, int64_t& waitTime); + std::shared_ptr lastPointerEvent_; + std::shared_ptr lastKeyEvent_; + int64_t lastInjectEventDelay_; + std::atomic isReplaying_; + int64_t timeOffset_; +}; +} // namespace MMI +} // namespace OHOS +#endif // EVENT_REPLAYER_H \ No newline at end of file diff --git a/tools/inject_event/src/event_replayer.cpp b/tools/inject_event/src/event_replayer.cpp new file mode 100644 index 0000000000..9e22f43acd --- /dev/null +++ b/tools/inject_event/src/event_replayer.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (c) 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 "event_replayer.h" + +#include "input_manager.h" +#include "util_ex.h" + +#undef MMI_LOG_TAG +#define MMI_LOG_TAG "EventReplayer" + +namespace OHOS { +namespace MMI { +namespace { +static constexpr uint32_t MAGIC_NUMBER = 0x4D4D4952; // "MMIR" in ASCII +static constexpr int32_t EVENT_BUFFER_SIZE = 10240; +} + +EventReplayer::EventReplayer() : lastPointerEvent_(nullptr), lastKeyEvent_(nullptr), + lastInjectEventDelay_(0), isReplaying_(false), timeOffset_(0) {} +EventReplayer::~EventReplayer() +{ + Stop(); + lastPointerEvent_ = nullptr; + lastKeyEvent_ = nullptr; +} + +bool EventReplayer::InitializeEventObjects() +{ + lastPointerEvent_ = PointerEvent::Create(); + if (!lastPointerEvent_) { + MMI_HILOGE("Failed to create pointer event object"); + return false; + } + lastKeyEvent_ = KeyEvent::Create(); + if (!lastKeyEvent_) { + MMI_HILOGE("Failed to create key event object"); + lastPointerEvent_ = nullptr; + return false; + } + return true; +} + +bool EventReplayer::ReadFileHeader(std::ifstream& eventFile, FileHeader& header) +{ + eventFile.read(reinterpret_cast(&header), sizeof(header)); + if (!eventFile.good()) { + return false; + } + if (header.magicNumber != MAGIC_NUMBER) { + MMI_HILOGE("Invalid file format: incorrect magic number"); + return false; + } + if (header.eventCount <= 0) { + MMI_HILOGW("Event file contains events count : %{public}d", header.eventCount); + return false; + } + return true; +} + +bool EventReplayer::ReplayEvents(std::ifstream& eventFile, const FileHeader& header) +{ + isReplaying_ = true; + int64_t lastEventTime = 0; + std::shared_ptr eventPtr = nullptr; + int32_t i = 0; + while (i < header.eventCount && isReplaying_ && ReadEvent(eventFile, eventPtr)) { + int64_t timestamp = eventPtr->GetActionTime(); + if (i == 0) { + int64_t firstEventOriginalTime = eventPtr->GetActionTime(); + int64_t replayStartTime = GetSysClockTime(); + timeOffset_ = replayStartTime - firstEventOriginalTime; + } + int64_t waitTime = 0; + if (ShouldWait(timestamp, lastEventTime, waitTime)) { + std::this_thread::sleep_for(std::chrono::microseconds(waitTime)); + } + lastEventTime = timestamp; + eventPtr->SetActionTime(eventPtr->GetActionTime() + timeOffset_); + eventPtr->SetActionStartTime(eventPtr->GetActionStartTime() + timeOffset_); + int64_t injectStartTime = GetSysClockTime(); + if (!InjectEvent(eventPtr->GetEventType())) { + MMI_HILOGW("Failed to inject event"); + break; + } + lastInjectEventDelay_ = GetSysClockTime() - injectStartTime; + i++; + } + Stop(); + int32_t successCount = i; + bool isFinished = (i == header.eventCount); + if (!isFinished) { + successCount = i - 1; + } + MMI_HILOGI("Replay completed: %{public}d/%{public}d", successCount, header.eventCount); + return isFinished; +} + +void EventReplayer::Stop() +{ + if (isReplaying_) { + MMI_HILOGI("Replay %{public}s", isReplaying_ ? "stopped" : "completed"); + isReplaying_ = false; + } +} + +bool EventReplayer::ReplayFile(const std::string& filePath) +{ + CALL_DEBUG_ENTER; + if (isReplaying_) { + MMI_HILOGI("Already replaying, cannot start new replay"); + return false; + } + std::ifstream eventFile(filePath, std::ios::binary); + if (!eventFile.is_open()) { + MMI_HILOGE("Failed to open event file: %{public}s", filePath.c_str()); + return false; + } + FileHeader header; + if (!ReadFileHeader(eventFile, header)) { + MMI_HILOGE("Invalid file header in: %{public}s", filePath.c_str()); + eventFile.close(); + return false; + } + if (!InitializeEventObjects()) { + MMI_HILOGE("Failed to initialize event objects"); + eventFile.close(); + return false; + } + MMI_HILOGI("Starting replay from file: %{public}s", filePath.c_str()); + bool ret = ReplayEvents(eventFile, header); + eventFile.close(); + return ret; +} + +bool EventReplayer::ReadEvent(std::ifstream& in, std::shared_ptr& eventPtr) +{ + Parcel parcel; + int32_t eventType = 0; + uint32_t dataSize = 0; + char buffer[EVENT_BUFFER_SIZE]; + in.read(reinterpret_cast(&eventType), sizeof(eventType)); + if (!in.good()) { + return false; + } + in.read(reinterpret_cast(&dataSize), sizeof(dataSize)); + if (!in.good() || dataSize > EVENT_BUFFER_SIZE) { + MMI_HILOGD("Data size too large or read failed: %{public}u", dataSize); + return false; + } + in.read(buffer, dataSize); + if (!in.good()) { + return false; + } + WRITEBUFFER(parcel, reinterpret_cast(buffer), static_cast(dataSize)); + + if (eventType == InputEvent::EVENT_TYPE_POINTER) { + lastPointerEvent_ = PointerEvent::Create(); + if (!lastPointerEvent_ || !lastPointerEvent_->ReadFromParcel(parcel)) { + return false; + } + eventPtr = lastPointerEvent_; + } else if (eventType == InputEvent::EVENT_TYPE_KEY) { + lastKeyEvent_ = KeyEvent::Create(); + if (!lastKeyEvent_ || !lastKeyEvent_->ReadFromParcel(parcel)) { + return false; + } + eventPtr = lastKeyEvent_; + } else { + MMI_HILOGD("Unknown input event type: %{public}d", eventType); + return false; + } + return true; +} + +bool EventReplayer::InjectEvent(int32_t eventType) +{ + if (eventType == InputEvent::EVENT_TYPE_POINTER) { + InputManager::GetInstance()->SimulateInputEvent(lastPointerEvent_, false); + return true; + } else if (eventType == InputEvent::EVENT_TYPE_KEY) { + InputManager::GetInstance()->SimulateInputEvent(lastKeyEvent_); + return true; + } else { + MMI_HILOGD("Unknown input event type: %{public}d", eventType); + } + return false; +} + +bool EventReplayer::ShouldWait(int64_t currentEventTime, int64_t lastEventTime, int64_t& waitTime) +{ + if (lastEventTime <= 0) { + waitTime = 0; + return false; + } + int64_t currentSysTime = GetSysClockTime(); + int64_t expectedExecutionTime = currentEventTime + timeOffset_; + if (currentSysTime >= expectedExecutionTime) { + MMI_HILOGD("Current time already past expected execution time, executing immediately"); + waitTime = 0; + return false; + } + int64_t eventInterval = currentEventTime - lastEventTime; + waitTime = eventInterval - lastInjectEventDelay_; + if (waitTime <= 0) { + waitTime = 0; + return false; + } else { + int64_t timeToExpected = expectedExecutionTime - currentSysTime; + if (waitTime > timeToExpected) { + MMI_HILOGD("Reducing wait time from %{public}lld to %{public}lld" + "microseconds to match expected execution time", + static_cast(waitTime), static_cast(timeToExpected)); + waitTime = timeToExpected; + } + return true; + } +} +} // namespace MMI +} // namespace OHOS \ No newline at end of file diff --git a/tools/inject_event/src/input_manager_command.cpp b/tools/inject_event/src/input_manager_command.cpp index f5342458af..b425f8714d 100644 --- a/tools/inject_event/src/input_manager_command.cpp +++ b/tools/inject_event/src/input_manager_command.cpp @@ -24,6 +24,8 @@ #include "event_log_helper.h" #include "hos_key_event.h" #include "input_manager.h" +#include "event_recorder.h" +#include "event_replayer.h" #undef MMI_LOG_TAG #define MMI_LOG_TAG "InputManagerCommand" @@ -196,6 +198,28 @@ int32_t InputManagerCommand::NextPos(int64_t begTimeMs, int64_t curtTimeMs, int3 return retPos < endPos ? endPos : retPos; } +bool CopyFile(const std::string& sourcePath, const std::string& destinationPath) +{ + std::ifstream source(sourcePath, std::ios::binary); + if (!source) { + std::cerr << "can't open source file: " << sourcePath << std::endl; + return false; + } + std::ofstream destination(destinationPath, std::ios::binary); + if (!destination) { + std::cerr << "can't open destination file: " << destinationPath << std::endl; + return false; + } + destination << source.rdbuf(); + if (source.fail() || destination.fail()) { + std::cerr << "copy error" << std::endl; + return false; + } + source.close(); + destination.close(); + return true; +} + int32_t InputManagerCommand::ParseCommand(int32_t argc, char *argv[]) { struct option headOptions[] = { @@ -205,6 +229,8 @@ int32_t InputManagerCommand::ParseCommand(int32_t argc, char *argv[]) {"touch", no_argument, nullptr, 'T'}, {"touchpad", no_argument, nullptr, 'P'}, {"joystick", no_argument, nullptr, 'J'}, + {"record", required_argument, nullptr, 'R'}, + {"replay", required_argument, nullptr, 'Y'}, {"help", no_argument, nullptr, '?'}, {nullptr, 0, nullptr, 0} }; @@ -250,7 +276,7 @@ int32_t InputManagerCommand::ParseCommand(int32_t argc, char *argv[]) int32_t c = 0; int32_t optionIndex = 0; optind = 0; - if ((c = getopt_long(argc, argv, "JKMPST?", headOptions, &optionIndex)) != -1) { + if ((c = getopt_long(argc, argv, "JKMPST?R:Y:", headOptions, &optionIndex)) != -1) { switch (c) { case 'M': { int32_t px = 0; @@ -1705,6 +1731,35 @@ int32_t InputManagerCommand::ParseCommand(int32_t argc, char *argv[]) } break; } + case 'R': { + const char* EVENT_FILE_NAME = "/data/service/el1/public/multimodalinput/event.tmp"; + std::cout<<"start record to file: "<SetEventRecorderEnable(true) != RET_OK) { + std::cout << "Failed start record" << std::endl; + return RET_ERR; + } + std::cout<< "Press enter key to stop playback" <SetEventRecorderEnable(false) != RET_OK) { + std::cout << "Failed stop record!" << std::endl; + return RET_ERR; + } + if (!CopyFile(EVENT_FILE_NAME, optarg)) { + return RET_ERR; + } + std::cout<<"record file finished!"<