diff --git a/bundle.json b/bundle.json index d39633b50e595c7d5e729f76643e66ed6dd93145..b2ab35483623f29c40d42c489996b3a6d012b949 100644 --- a/bundle.json +++ b/bundle.json @@ -100,7 +100,8 @@ "//base/msdp/device_status/frameworks/js/napi/boomerang:metadatabinding_napi", "//base/msdp/device_status/frameworks/js/napi/device_status:devicestatus_napi", "//base/msdp/device_status/frameworks/js/napi/underage_model:userstatus_napi", - "//base/msdp/device_status/frameworks/ets/drag:dragInteraction" + "//base/msdp/device_status/frameworks/ets/drag:dragInteraction", + "//base/msdp/device_status/frameworks/ets/coop:msdp_cooperate_group" ], "service_group":[ "//base/msdp/device_status/libs:devicestatus_algo", diff --git a/frameworks/ets/coop/BUILD.gn b/frameworks/ets/coop/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..b14aae60c723d9b52d1b2ee1eb40b53c10f51dce --- /dev/null +++ b/frameworks/ets/coop/BUILD.gn @@ -0,0 +1,108 @@ +# 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. + +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("//build/ohos/taihe_idl/taihe.gni") +import("//base/msdp/device_status/device_status.gni") + +config("my_headers") { + include_dirs = [ "${device_status_root_path}/utils/common/include" ] +} +copy_taihe_idl("coop_taihe") { + sources = [ "idl/ohos.multimodalInput.inputDeviceCooperate.taihe" ] +} +subsystem_name = "msdp" +part_name = "device_status" +taihe_generated_file_path = "$taihe_file_path/out/$subsystem_name/$part_name/coop" +ohos_taihe("run_taihe") { + taihe_generated_file_path = "$taihe_generated_file_path" + deps = [ ":coop_taihe" ] + outputs = [ + "$taihe_generated_file_path/src/ohos.multimodalInput.inputDeviceCooperate.ani.cpp", + "$taihe_generated_file_path/src/ohos.multimodalInput.inputDeviceCooperate.abi.c", + ] +} +taihe_shared_library("Cooperate") { + taihe_generated_file_path = "$taihe_generated_file_path" + part_name = "$part_name" + subsystem_name = "$subsystem_name" + sources = get_target_outputs(":run_taihe") + branch_protector_ret = "pac_ret" + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + } + + configs = [ + ":my_headers", + ] + + include_dirs = [ + "include", + "${device_status_utils_path}/include", + "${device_status_interfaces_path}/innerkits/interaction/include", + "${device_status_frameworks_path}/native/interaction/include", + "${device_status_frameworks_path}/base/msdp/device_status/frameworks/ets/coop/include/ohos.multimodalInput.inputDeviceCooperate.impl.h", + "${device_status_frameworks_path}/base/msdp/device_status/interfaces/innerkits/interaction/include/i_coordination_listener.h", + ] + external_deps = [ + "c_utils:utils", + "graphic_2d:librender_service_client", + "graphic_2d:librender_service_base", + "hilog:libhilog" + ] + sources += [ + "src/ani_constructor.cpp", + "src/ani_cooperate_manager.cpp", + "src/ani_event_cooperate_target.cpp", + "src/cooperate_common.cpp", + "src/ohos.multimodalInput.inputDeviceCooperate.impl.cpp", + ] + deps = [ + ":run_taihe", + "${device_status_interfaces_path}/innerkits:devicestatus_client", + "${device_status_utils_path}:devicestatus_util", + ] +} +generate_static_abc("coop_abc") { + base_url = "$taihe_generated_file_path" + files = [ "$taihe_generated_file_path/@ohos.multimodalInput.inputDeviceCooperate.ets" ] + is_boot_abc = "True" + device_dst_file = "/system/framework/coop_abc.abc" + dependencies = [ ":run_taihe" ] +} +ohos_prebuilt_etc("coop_etc") { + source = "$target_out_dir/coop_abc.abc" + module_install_dir = "framework" + part_name = "$part_name" + subsystem_name = "$subsystem_name" + deps = [ ":coop_abc" ] +} + +generate_static_abc("coop_taihe_test") { + base_url = "${device_status_root_path}/frameworks/ets/coop" + files = [ "$base_url/test/test_main.ets" ] + is_boot_abc = "True" + device_dst_file = "/system/framework/coop_taihe_test.abc" +} + +group("msdp_cooperate_group") { + deps = [ + ":coop_etc", + ":Cooperate", + ":coop_taihe_test", + ] +} diff --git a/frameworks/ets/coop/idl/ohos.multimodalInput.inputDeviceCooperate.taihe b/frameworks/ets/coop/idl/ohos.multimodalInput.inputDeviceCooperate.taihe new file mode 100644 index 0000000000000000000000000000000000000000..5d737d6ae5acb4df5addf68c85b493db1160fb09 --- /dev/null +++ b/frameworks/ets/coop/idl/ohos.multimodalInput.inputDeviceCooperate.taihe @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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. + */ + +@!namespace("@ohos.multimodalInput.inputDeviceCooperate", "inputDeviceCooperate") +@!sts_inject(""" +static{ loadLibrary("Cooperate.z")} +""") +@!sts_inject_into_module("import { AsyncCallback } from '@ohos.base';") + +enum EventMsg : i32 { + MSG_COOPERATE_INFO_START = 200, + MSG_COOPERATE_INFO_SUCCESS = 201, + MSG_COOPERATE_INFO_FAIL = 202, + MSG_COOPERATE_STATE_ON = 500, + MSG_COOPERATE_STATE_OFF = 501, +} + +@rename("enable") +function EnableAsync(enableInput: bool, opq: @sts_type("AsyncCallback") Opaque): void; +@rename("enable") +function EnablePromise(enableInput: bool): @sts_type("Promise") Opaque; + +@rename("start") +function StartAsync(sinkDeviceDescriptor: String, srcInputDeviceId: i32, opq: @sts_type("AsyncCallback") Opaque): void; +@rename("start") +function StartPromise(sinkDeviceDescriptor: String, srcInputDeviceId: i32): @sts_type("Promise") Opaque; + +@rename("stop") +function StopAsync(opq: @sts_type("AsyncCallback") Opaque): void; +@rename("stop") +function StopPromise(): @sts_type("Promise") Opaque; + +@rename("getState") +function GetStateAsync(deviceDescriptor: String, opq: @sts_type("AsyncCallback") Opaque): void; +@rename("getState") +function GetStatePromise(deviceDescriptor: String): @sts_type("Promise") Opaque; + +struct Coopinfo { + deviceDescriptor: String; + eventMsg: EventMsg; +} + +@!sts_inject(""" + function on(type:'cooperation', callback: AsyncCallback) { + return onCooperation(callback); + } + function off(type: 'cooperation', callback?: AsyncCallback) { + return offCooperation(callback); + } +""") + +function onCooperation(opq: @sts_type("AsyncCallback") Opaque); +function offCooperation(opq: Optional<@sts_type("AsyncCallback") Opaque>); + + + + + diff --git a/frameworks/ets/coop/include/ani_cooperate_manager.h b/frameworks/ets/coop/include/ani_cooperate_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..99b1a9c16d021949cf8f8da1cf94ccb1f6e14d01 --- /dev/null +++ b/frameworks/ets/coop/include/ani_cooperate_manager.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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. + */ + +#ifndef ANI_COOPERATE_MANAGER_H +#define ANI_COOPERATE_MANAGER_H + +#include +#include + +#include "cooperate_common.h" +#include "ani_event_cooperate_target.h" + +namespace OHOS { +namespace Msdp { +namespace DeviceStatus { +class AniCooperateManager final { +public: + enum class CooperateMessage { + INFO_START = 0, + INFO_SUCCESS = 1, + INFO_FAIL = 2, + STATE_ON = 3, + STATE_OFF = 4 + }; + static AniCooperateManager& GetInstance(); + AniCooperateManager(); + DISALLOW_COPY_AND_MOVE(AniCooperateManager); + ~AniCooperateManager(); + void Enable(bool enable, uintptr_t opq, ani_object& promise); + void Start(const std::string &remoteNetworkDescriptor, + int32_t startDeviceId, uintptr_t opq, ani_object& promise); + void Stop(uintptr_t opq, ani_object& promise); + void GetState(const std::string &deviceDescriptor, uintptr_t opq, ani_object& promise); + void OnCooperation(uintptr_t opq); + void OffCooperation(::taihe::optional_view opq); + void OnCoordinationMessage(const std::string &networkId, CoordinationMessage msg); + void EmitCoordinationMessageEvent(std::shared_ptr cb); +protected: + void EmitAni(std::shared_ptr cb); + void EmitAniPromise(std::shared_ptr cb); + void EmitAniAsyncCallback(std::shared_ptr cb); + void EmitGetState(std::shared_ptr cb); +private: + std::mutex mutex_; + std::map>> + coordinationListeners_ {}; + std::atomic_bool isListeningProcess_ { false }; + std::shared_ptr listener_ {nullptr}; + inline static std::map messageTransform_ = { + { CoordinationMessage::PREPARE, CooperateMessage::STATE_ON }, + { CoordinationMessage::UNPREPARE, CooperateMessage::STATE_OFF }, + { CoordinationMessage::ACTIVATE, CooperateMessage::INFO_START }, + { CoordinationMessage::ACTIVATE_SUCCESS, CooperateMessage::INFO_SUCCESS }, + { CoordinationMessage::ACTIVATE_FAIL, CooperateMessage::INFO_FAIL } + }; +}; +#define ANI_COOPERATE_MGR AniCooperateManager::GetInstance() +} // namespace DeviceStatus +} // namespace Msdp +} // namespace OHOS +#endif // JS_COOPERATE_MANAGER_H diff --git a/frameworks/ets/coop/include/ani_event_cooperate_target.h b/frameworks/ets/coop/include/ani_event_cooperate_target.h new file mode 100644 index 0000000000000000000000000000000000000000..e18a2db70e42ea99712ddff04e20bdc8e14c827d --- /dev/null +++ b/frameworks/ets/coop/include/ani_event_cooperate_target.h @@ -0,0 +1,43 @@ +/* + * 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 ANI_EVENT_COOPERATE_TARGET_H +#define ANI_EVENT_COOPERATE_TARGET_H + +#include "nocopyable.h" +#include "i_coordination_listener.h" + +namespace OHOS { +namespace Msdp { +namespace DeviceStatus { +class AniEventCooperateTarget : public ICoordinationListener, + public std::enable_shared_from_this { +public: + enum class CooperateMessage { + INFO_START = 0, + INFO_SUCCESS = 1, + INFO_FAIL = 2, + STATE_ON = 3, + STATE_OFF = 4 + }; + AniEventCooperateTarget(); + DISALLOW_COPY_AND_MOVE(AniEventCooperateTarget); + virtual ~AniEventCooperateTarget() = default; + void OnCoordinationMessage(const std::string &networkId, CoordinationMessage msg) override; +}; +} // namespace DeviceStatus +} // namespace Msdp +} // namespace OHOS +#endif // JS_EVENT_COOPERATE_TARGET_H diff --git a/frameworks/ets/coop/include/cooperate_common.h b/frameworks/ets/coop/include/cooperate_common.h new file mode 100644 index 0000000000000000000000000000000000000000..75cd432c98ab64e67c3a951239fbdd08f768f396 --- /dev/null +++ b/frameworks/ets/coop/include/cooperate_common.h @@ -0,0 +1,133 @@ +/* + * 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 INPUT_DEVICE_COOPERATE_ANI_H +#define INPUT_DEVICE_COOPERATE_ANI_H + +#include +#include + +#include "ohos.multimodalInput.inputDeviceCooperate.proj.hpp" +#include "ohos.multimodalInput.inputDeviceCooperate.impl.hpp" +#include "ohos.multimodalInput.inputDeviceCooperate.Coopinfo.ani.1.hpp" +#include "taihe/runtime.hpp" +#include "stdexcept" + +#include "coordination_message.h" +#include "devicestatus_errors.h" +#include "fi_log.h" + +namespace OHOS { +namespace Msdp { +namespace DeviceStatus { +#ifndef RET_OK + #define RET_OK (0) +#endif + +const std::map NAPI_ERRORS = { + { COMMON_PERMISSION_CHECK_ERROR, "Permission denied. An attempt was made to %s forbidden by permission:%s." }, + { COMMON_PARAMETER_ERROR, "Parameter error. The type of %s must be %s." }, + { COMMON_NOT_ALLOWED_DISTRIBUTED, "Cross-device dragging is not allowed" }, + { COOPERATOR_FAIL, " Service exception. Possible causes: 1. A system error, such as null pointer," + "container-related exception, or IPC exception. 2. N-API invocation exception or invalid N-API status." }, + { COMMON_NOT_SYSTEM_APP, "Non system applications." } +}; + +const std::unordered_map COOPERATE_MSG_MAP { + { CoordinationMessage::PREPARE, "PREPARE" }, + { CoordinationMessage::UNPREPARE, "UNPREPARE" }, + { CoordinationMessage::ACTIVATE, "ACTIVATE" }, + { CoordinationMessage::ACTIVATE_SUCCESS, "ACTIVATE_SUCCESS" }, + { CoordinationMessage::ACTIVATE_FAIL, "ACTIVATE_FAIL" }, + { CoordinationMessage::DEACTIVATE_SUCCESS, "DEACTIVATE_SUCCESS" }, + { CoordinationMessage::DEACTIVATE_FAIL, "DEACTIVATE_FAIL" }, + { CoordinationMessage::SESSION_CLOSED, "SESSION_CLOSED" } +}; + + +enum class CallbackType : uint8_t { + ENABLE, + START, + STOP, + GETSTATE, + ON, + OFF, +}; + + +class AniCallbackInfo { +public: + AniCallbackInfo(CallbackType t):type_(t) {} + ~AniCallbackInfo(); + + bool init(uintptr_t opq); + void AttachThread(); + void DetachThread(); + + int32_t errCode_ { 0 }; + bool result_ = false; + std::string deviceDescriptor_; + CoordinationMsgInfo msgInfo_; + CallbackType type_; + ani_object funObject_ = nullptr; + ani_ref ref_ = nullptr; + ani_vm* vm_ = nullptr; + ani_env* envT_ = nullptr; + ani_env* env_ = nullptr; + ani_object promise_ = nullptr; + ani_resolver deferred_ = nullptr; + bool attach_ = false; +}; + +class CooperateCommon final { +public: + static ani_object WrapBusinessError(ani_env* env, const std::string& msg); + static ani_ref CreateBusinessError(ani_env* env, ani_int code, const std::string& msg); + static ani_status GetAniEnv(ani_vm* vm, ani_env** env); + static void ExecAsyncCallbackPromise(ani_env *env, ani_resolver deferred, ani_ref data, ani_ref businessError); + static ani_status ExecAsyncCallBack(ani_env *env, ani_object businessError, + ani_object param, ani_object callbackFunc); + static ani_object CreateBooleanObject(ani_env *env, bool value); + static ani_object CreateIntObject(ani_env *env, int32_t value); + static bool GetErrorMsg(int32_t code, std::string &codeMsg); + static void HandleExecuteResult(int32_t errCode, const std::string param1, const std::string param2); + static bool GetErrMsg(const CoordinationMsgInfo &msgInfo, std::string &msg); + static int32_t GetErrCode(const CoordinationMsgInfo &msgInfo); + static bool IsSameHandle(ani_env *env, ani_ref localRef, ani_ref inRef); +}; + +class GlobalRefGuard { + ani_env *env_ = nullptr; + ani_ref ref_ = nullptr; + +public: + GlobalRefGuard(ani_env *env, ani_object obj); + explicit operator bool() const + { + return ref_ != nullptr; + } + ani_ref get() const + { + return ref_; + } + ~GlobalRefGuard(); + GlobalRefGuard(const GlobalRefGuard &) = delete; + GlobalRefGuard &operator=(const GlobalRefGuard &) = delete; +}; + +} // DeviceStatus +} // Msdp +} // OHOS +#endif // INPUT_DEVICE_COOPERATE_ANI_H \ No newline at end of file diff --git a/frameworks/ets/coop/src/ani_constructor.cpp b/frameworks/ets/coop/src/ani_constructor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c429264ebf910075c4edae7c15cc45e5f95cab9c --- /dev/null +++ b/frameworks/ets/coop/src/ani_constructor.cpp @@ -0,0 +1,31 @@ +/* + * 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 "ohos.multimodalInput.inputDeviceCooperate.ani.hpp" + +ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result) +{ + ani_env *env; + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + return ANI_ERROR; + } + if (ANI_OK != ohos::multimodalInput::inputDeviceCooperate::ANIRegister(env)) { + std::cerr << "Error from ohos::deviceStatus::dragInteraction::ANIRegister" << std::endl; + return ANI_ERROR; + } + *result = ANI_VERSION_1; + return ANI_OK; +} +// ohos.multimodalInput.inputDeviceCooperate \ No newline at end of file diff --git a/frameworks/ets/coop/src/ani_cooperate_manager.cpp b/frameworks/ets/coop/src/ani_cooperate_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3cf1c5df180a12a035b32046de8929f3acb5a79a --- /dev/null +++ b/frameworks/ets/coop/src/ani_cooperate_manager.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (c) 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 "ani_cooperate_manager.h" + +#include "devicestatus_define.h" +#include "interaction_manager.h" + + +#undef LOG_TAG +#define LOG_TAG "AniCooperateManager" + +namespace OHOS { +namespace Msdp { +namespace DeviceStatus { + +namespace { +const std::string COORDINATION = "cooperation"; +} + +AniCooperateManager::AniCooperateManager() +{ + CALL_DEBUG_ENTER; + auto ret = coordinationListeners_.insert({ COORDINATION, + std::vector>()}); + if (!ret.second) { + FI_HILOGW("Failed to add listener, errCode:%{public}d", static_cast(DeviceStatus::VAL_NOT_EXP)); + } +} + +AniCooperateManager::~AniCooperateManager() +{ + CALL_INFO_TRACE; + std::lock_guard guard(mutex_); + coordinationListeners_.clear(); + if (isListeningProcess_) { + isListeningProcess_ = false; + if (listener_) { + INTERACTION_MGR->UnregisterCoordinationListener(listener_); + listener_ = nullptr; + } + } +} + +AniCooperateManager& AniCooperateManager::GetInstance() +{ + static AniCooperateManager instance; + return instance; +} + +void AniCooperateManager::EmitAniPromise(std::shared_ptr cb) +{ + CALL_DEBUG_ENTER; + if (cb == nullptr) { + FI_HILOGE("cb is nullptr"); + return; + } + if (cb->promise_ != nullptr) { + FI_HILOGE("promise is nullptr"); + return; + } + auto env = cb->envT_; + if (cb->result_) { + CooperateCommon::ExecAsyncCallbackPromise(env, cb->deferred_, nullptr, nullptr); + return; + } + std::string errMsg; + if (!CooperateCommon::GetErrMsg(cb->msgInfo_, errMsg)) { + FI_HILOGE("GetErrMsg failed"); + return; + } + int32_t errorCode = CooperateCommon::GetErrCode(cb->msgInfo_); + FI_HILOGD("errorCode:%{public}d, msg:%{public}s", errorCode, errMsg.c_str()); + auto callResult = CooperateCommon::CreateBusinessError(env, static_cast(errorCode), errMsg); + if (callResult == nullptr) { + FI_HILOGE("The callResult is nullptr"); + return; + } + CooperateCommon::ExecAsyncCallbackPromise(env, cb->deferred_, callResult, nullptr); + return; +} + +void AniCooperateManager::EmitAniAsyncCallback(std::shared_ptr cb) +{ + CALL_DEBUG_ENTER; + if (cb == nullptr) { + FI_HILOGE("cb is nullptr"); + return; + } + if (cb->funObject_ != nullptr) { + FI_HILOGE("promise is nullptr"); + return; + } + auto env = cb->envT_; + ani_ref aniUndefined; + ani_ref aniNull; + ani_status status; + if ((status = env->GetUndefined(&aniUndefined)) != ANI_OK) { + FI_HILOGE("get undefined value failed, status = %{public}d", status); + return; + } + if ((status = env->GetNull(&aniNull)) != ANI_OK) { + FI_HILOGE("get undefined value failed, status = %{public}d", status); + return; + } + if (cb->result_) { + CooperateCommon::ExecAsyncCallBack(env, static_cast(aniNull), + static_cast(aniUndefined), cb->funObject_); + return; + } + std::string errMsg; + if (!CooperateCommon::GetErrMsg(cb->msgInfo_, errMsg)) { + FI_HILOGE("GetErrMsg failed"); + return; + } + int32_t errorCode = CooperateCommon::GetErrCode(cb->msgInfo_); + FI_HILOGD("errorCode:%{public}d, msg:%{public}s", errorCode, errMsg.c_str()); + auto callResult = CooperateCommon::CreateBusinessError(env, static_cast(errorCode), errMsg); + if (callResult == nullptr) { + FI_HILOGE("The callResult is nullptr"); + return; + } + CooperateCommon::ExecAsyncCallBack(env, static_cast(callResult), + static_cast(aniUndefined), cb->funObject_); + return; +} + +void AniCooperateManager::EmitAni(std::shared_ptr cb) +{ + CALL_DEBUG_ENTER; + if (cb == nullptr) { + FI_HILOGE("cb is nullptr"); + return; + } + if (cb->promise_ != nullptr) { + EmitAniPromise(cb); + return; + } + EmitAniAsyncCallback(cb); +} + +void AniCooperateManager::EmitGetState(std::shared_ptr cb) +{ + CALL_DEBUG_ENTER; + if (cb == nullptr) { + FI_HILOGE("cb is nullptr"); + return; + } + auto env = cb->envT_; + ani_ref aniNull; + ani_status status; + ani_object aniResult = CooperateCommon::CreateBooleanObject(env, cb->result_); + if (aniResult == nullptr) { + FI_HILOGE("create boolean object is null"); + return; + } + if ((status = env->GetNull(&aniNull)) != ANI_OK) { + FI_HILOGE("get undefined value failed, status = %{public}d", status); + return; + } + if (cb->promise_ != nullptr) { + CooperateCommon::ExecAsyncCallbackPromise(env, cb->deferred_, static_cast(aniNull), nullptr); + return; + } + CooperateCommon::ExecAsyncCallBack(env, static_cast(aniNull), aniResult, cb->funObject_); + return; +} + +void AniCooperateManager::Enable(bool enable, uintptr_t opq, ani_object& promise) +{ + CALL_DEBUG_ENTER; + std::shared_ptr cb = std::make_shared(CallbackType::ENABLE); + if (nullptr == cb) { + FI_HILOGE("cbInfo is null"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + if (!cb->init(opq)) { + FI_HILOGE("init err"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + promise = cb->promise_; + auto callback = [this, cb](const std::string &networkId, const CoordinationMsgInfo &msgInfo) { + CALL_DEBUG_ENTER; + cb->result_ = (msgInfo.msg == CoordinationMessage::PREPARE || + msgInfo.msg == CoordinationMessage::UNPREPARE); + cb->msgInfo_ = msgInfo; + cb->AttachThread(); + EmitAni(cb); + cb->DetachThread(); + }; + int32_t errCode = 0; + if (enable) { + errCode = INTERACTION_MGR->PrepareCoordination(callback); + } else { + errCode = INTERACTION_MGR->UnprepareCoordination(callback); + } + if (errCode != RET_OK) { + CooperateCommon::HandleExecuteResult(errCode, "enable", COOPERATE_PERMISSION); + } + return; +} + +void AniCooperateManager::Start(const std::string &remoteNetworkDescriptor, + int32_t startDeviceId, uintptr_t opq, ani_object& promise) +{ + CALL_DEBUG_ENTER; + std::shared_ptr cb = std::make_shared(CallbackType::START); + if (nullptr == cb) { + FI_HILOGE("cbInfo is null"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + if (!cb->init(opq)) { + FI_HILOGE("init err"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + promise = cb->promise_; + auto callback = [this, cb](const std::string &networkId, const CoordinationMsgInfo &msgInfo) { + CALL_DEBUG_ENTER; + cb->result_ = (msgInfo.msg == CoordinationMessage::ACTIVATE_SUCCESS); + cb->msgInfo_ = msgInfo; + cb->AttachThread(); + EmitAni(cb); + cb->DetachThread(); + }; + int32_t errCode = 0; + errCode = INTERACTION_MGR->ActivateCoordination(remoteNetworkDescriptor, startDeviceId, callback); + if (errCode != RET_OK) { + CooperateCommon::HandleExecuteResult(errCode, "start", COOPERATE_PERMISSION); + } + return; +} + +void AniCooperateManager::Stop(uintptr_t opq, ani_object& promise) +{ + CALL_DEBUG_ENTER; + std::shared_ptr cb = std::make_shared(CallbackType::STOP); + if (nullptr == cb) { + FI_HILOGE("cbInfo is null"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + if (!cb->init(opq)) { + FI_HILOGE("init err"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + promise = cb->promise_; + auto callback = [this, cb](const std::string &networkId, const CoordinationMsgInfo &msgInfo) { + CALL_DEBUG_ENTER; + cb->result_ = (msgInfo.msg == CoordinationMessage::DEACTIVATE_SUCCESS); + cb->msgInfo_ = msgInfo; + cb->AttachThread(); + EmitAni(cb); + cb->DetachThread(); + }; + bool isUnchained = false; + int32_t errCode = 0; + errCode = INTERACTION_MGR->DeactivateCoordination(isUnchained, callback); + if (errCode != RET_OK) { + CooperateCommon::HandleExecuteResult(errCode, "stop", COOPERATE_PERMISSION); + } + return; +} + +void AniCooperateManager::GetState(const std::string &deviceDescriptor, uintptr_t opq, ani_object& promise) +{ + CALL_DEBUG_ENTER; + std::shared_ptr cb = std::make_shared(CallbackType::GETSTATE); + if (nullptr == cb) { + FI_HILOGE("cbInfo is null"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + if (!cb->init(opq)) { + FI_HILOGE("init err"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + promise = cb->promise_; + auto callback = [this, cb](bool state) { + CALL_DEBUG_ENTER; + cb->result_ = state; + cb->AttachThread(); + EmitGetState(cb); + cb->DetachThread(); + }; + int32_t errCode = 0; + errCode = INTERACTION_MGR->GetCoordinationState(deviceDescriptor, callback); + if (errCode != RET_OK) { + CooperateCommon::HandleExecuteResult(errCode, "getState", COOPERATE_PERMISSION); + } + return; +} + +void AniCooperateManager::OnCooperation(uintptr_t opq) +{ + CALL_DEBUG_ENTER; + std::lock_guard guard(mutex_); + std::shared_ptr cb = std::make_shared(CallbackType::ON); + if (nullptr == cb) { + FI_HILOGE("cbInfo is null"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + if (!cb->init(opq)) { + FI_HILOGE("init err"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "internal Error"); + return; + } + std::string type = COORDINATION; + auto iter = coordinationListeners_.find(type); + if (iter == coordinationListeners_.end()) { + FI_HILOGE("Failed to add listener, type:%{public}s", type.c_str()); + return; + } + for (const auto &item : iter->second) { + CHKPC(item); + if (CooperateCommon::IsSameHandle(cb->env_, item->ref_, cb->ref_)) { + FI_HILOGE("The handle already exists"); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "The handle already exists"); + return; + } + } + iter->second.push_back(cb); + if (!isListeningProcess_) { + isListeningProcess_ = true; + listener_ = std::make_shared(); + INTERACTION_MGR->RegisterCoordinationListener(listener_); + } +} + +void AniCooperateManager::OffCooperation(::taihe::optional_view opq) +{ + CALL_DEBUG_ENTER; + std::lock_guard guard(mutex_); + auto type = COORDINATION; + auto iter = coordinationListeners_.find(type); + if (iter == coordinationListeners_.end()) { + FI_HILOGE("Failed to remove listener, type:%{public}s", type.c_str()); + return; + } + ani_env *env = taihe::get_env(); + if (env == nullptr) { + FI_HILOGE("ani_env is nullptr!"); + return; + } + if (!opq.has_value()) { + iter->second.clear(); + } else { + GlobalRefGuard guard(env, reinterpret_cast(opq.value())); + if (!guard) { + FI_HILOGE("GlobalRefGuard is false!"); + return; + } + for (auto it = iter->second.begin(); it != iter->second.end();) { + bool isSame = CooperateCommon::IsSameHandle(env, (*it)->ref_, guard.get()); + if (isSame) { + FI_HILOGD("Successfully removed the listener"); + it = iter->second.erase(it); + } else { + ++it; + } + } + } + if (isListeningProcess_ && iter->second.empty()) { + isListeningProcess_ = false; + if (listener_) { + INTERACTION_MGR->UnregisterCoordinationListener(listener_); + listener_ = nullptr; + } + } +} + +void AniCooperateManager::EmitCoordinationMessageEvent(std::shared_ptr cb) +{ + CALL_DEBUG_ENTER; + if (cb == nullptr) { + FI_HILOGE("The data is nullptr"); + return; + } + CHKPV(cb->env_); + CHKPV(cb->envT_); + CHKPV(cb->funObject_); + auto env = cb->envT_; + auto iter = messageTransform_.find(cb->msgInfo_.msg); + if (iter == messageTransform_.end()) { + FI_HILOGE("Failed to find the message code"); + return; + } + using AniCoopInfo = ::ohos::multimodalInput::inputDeviceCooperate::Coopinfo; + using AniEvnetMsg = ::ohos::multimodalInput::inputDeviceCooperate::EventMsg; + AniCoopInfo info { + .deviceDescriptor = cb->deviceDescriptor_, + .eventMsg = AniEvnetMsg::from_value(static_cast(iter->second)) + }; + info.deviceDescriptor = cb->deviceDescriptor_; + auto param = ::taihe::into_ani(env, info); + ani_ref aniNull; + ani_status status; + if ((status = env->GetNull(&aniNull)) != ANI_OK) { + FI_HILOGE("get undefined value failed, status = %{public}d", status); + return; + } + CooperateCommon::ExecAsyncCallBack(env, static_cast(aniNull), + param, cb->funObject_); +} + +void AniCooperateManager::OnCoordinationMessage(const std::string &networkId, CoordinationMessage msg) +{ + CALL_INFO_TRACE; + std::lock_guard guard(mutex_); + auto changeEvent = coordinationListeners_.find(COORDINATION); + if (changeEvent == coordinationListeners_.end()) { + FI_HILOGE("Failed to find the %{public}s", std::string(COORDINATION).c_str()); + return; + } + for (auto &item : changeEvent->second) { + CHKPC(item); + CHKPC(item->env_); + item->msgInfo_.msg = msg; + item->deviceDescriptor_ = networkId; + item->AttachThread(); + EmitCoordinationMessageEvent(item); + item->DetachThread(); + } +} +} // namespace DeviceStatus +} // namespace Msdp +} // namespace OHOS diff --git a/frameworks/ets/coop/src/ani_event_cooperate_target.cpp b/frameworks/ets/coop/src/ani_event_cooperate_target.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e4498c82bec77a7e786b67ef5b7df44f92df165a --- /dev/null +++ b/frameworks/ets/coop/src/ani_event_cooperate_target.cpp @@ -0,0 +1,37 @@ +/* + * 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 "ani_event_cooperate_target.h" + +#include "interaction_manager.h" +#include "ani_cooperate_manager.h" + +#undef LOG_TAG +#define LOG_TAG "AniEventCooperateTarget" + +namespace OHOS { +namespace Msdp { +namespace DeviceStatus { +AniEventCooperateTarget::AniEventCooperateTarget() +{ +} + +void AniEventCooperateTarget::OnCoordinationMessage(const std::string &networkId, CoordinationMessage msg) +{ + ANI_COOPERATE_MGR.OnCoordinationMessage(networkId, msg); +} +} // namespace DeviceStatus +} // namespace Msdp +} // namespace OHOS diff --git a/frameworks/ets/coop/src/cooperate_common.cpp b/frameworks/ets/coop/src/cooperate_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17f0caa47d473b3f26ecef68af6d6af46ef2cab2 --- /dev/null +++ b/frameworks/ets/coop/src/cooperate_common.cpp @@ -0,0 +1,433 @@ +/* + * 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 "securec.h" + +#include "cooperate_common.h" +#include "fi_log.h" + + +#undef LOG_TAG +#define LOG_TAG "AniCoopCommon" + +namespace OHOS { +namespace Msdp { +namespace DeviceStatus { +namespace { +constexpr int32_t TWO_PARAMS = 2; +} + +AniCallbackInfo::~AniCallbackInfo() +{ + CALL_DEBUG_ENTER; + if (env_ && ref_) { + FI_HILOGD("Begin ANI reference delete!"); + env_->GlobalReference_Delete(ref_); + ref_ = nullptr; + } + if (attach_ && vm_) { + vm_->DetachCurrentThread(); + attach_ = false; + } +} + +bool AniCallbackInfo::init(uintptr_t opq) +{ + CALL_DEBUG_ENTER; + env_ = taihe::get_env(); + if (env_ == nullptr) { + FI_HILOGE("ANI Get env is nullptr"); + return false; + } + ani_status status = ANI_OK; + if (ANI_OK != env_->GetVM(&vm_)) { + FI_HILOGE("env GetVM faild"); + return false; + } + if (opq != 0) { + FI_HILOGD("beign init callback"); + funObject_ = reinterpret_cast(opq); + if ((status= env_->GlobalReference_Create(funObject_, &ref_)) != ANI_OK) { + FI_HILOGE("create callback object failed, status = %{public}d", status); + ref_ = nullptr; + return false; + } + } else { + if ((status = env_->Promise_New(&deferred_, &promise_)) != ANI_OK) { + FI_HILOGE("create promise object failed, status = %{public}d", status); + return false; + } + } + return true; +} + +void AniCallbackInfo::AttachThread() +{ + CALL_DEBUG_ENTER; + if (attach_) { + return; + } + ani_status status = ANI_OK; + if ((status = CooperateCommon::GetAniEnv(vm_, &envT_)) != ANI_OK) { + FI_HILOGE("create promise object failed, status = %{public}d", status); + return; + } + attach_ = true; +} + +void AniCallbackInfo::DetachThread() +{ + CALL_DEBUG_ENTER; + if (attach_ && vm_) { + vm_->DetachCurrentThread(); + attach_ = false; + } +} + +////////////////CooperateCommon////////////////////////////////////////////////// +ani_object CooperateCommon::WrapBusinessError(ani_env* env, const std::string& msg) +{ + ani_class cls {}; + ani_method method {}; + ani_object obj = nullptr; + ani_status status = ANI_ERROR; + if (env == nullptr) { + return nullptr; + } + + ani_string aniMsg = nullptr; + if ((status = env->String_NewUTF8(msg.c_str(), msg.size(), &aniMsg)) != ANI_OK) { + return nullptr; + } + + ani_ref undefRef; + if ((status = env->GetUndefined(&undefRef)) != ANI_OK) { + return nullptr; + } + + if ((status = env->FindClass("Lescompat/Error;", &cls)) != ANI_OK) { + return nullptr; + } + if ((status = env->Class_FindMethod(cls, "", "Lstd/core/String;Lescompat/ErrorOptions;:V", &method)) != + ANI_OK) { + return nullptr; + } + + if ((status = env->Object_New(cls, method, &obj, aniMsg, undefRef)) != ANI_OK) { + return nullptr; + } + return obj; +} + +ani_ref CooperateCommon::CreateBusinessError(ani_env* env, ani_int code, const std::string& msg) +{ + ani_class cls; + ani_status status = ANI_OK; + if ((status = env->FindClass("L@ohos/base/BusinessError;", &cls)) != ANI_OK) { + return nullptr; + } + ani_method ctor; + if ((status = env->Class_FindMethod(cls, "", "DLescompat/Error;:V", &ctor)) != ANI_OK) { + return nullptr; + } + ani_object error = CooperateCommon::WrapBusinessError(env, msg); + if (error == nullptr) { + return nullptr; + } + ani_object obj = nullptr; + ani_double dCode(code); + if ((status = env->Object_New(cls, ctor, &obj, dCode, error)) != ANI_OK) { + return nullptr; + } + return reinterpret_cast(obj); +} + +ani_status CooperateCommon::GetAniEnv(ani_vm* vm, ani_env** env) +{ + CALL_DEBUG_ENTER; + if (nullptr == vm) { + return ANI_ERROR; + } + ani_options aniOpt {0, nullptr}; + auto status = vm->AttachCurrentThread(&aniOpt, ANI_VERSION_1, env); + return status; +} + +void CooperateCommon::ExecAsyncCallbackPromise(ani_env *env, ani_resolver deferred, ani_ref data, ani_ref businessError) +{ + CALL_DEBUG_ENTER; + if (nullptr == env) { + FI_HILOGE("env is null"); + return; + } + if (nullptr == deferred) { + FI_HILOGE("deferred is null"); + return; + } + ani_status status = ANI_OK; + if (businessError != nullptr) { + if ((status = env->PromiseResolver_Reject(deferred, static_cast(businessError))) != ANI_OK) { + FI_HILOGE("promise reject failed, status = %{public}d", status); + } + return; + } + if (nullptr == data) { + if ((status = env->GetUndefined(&data)) != ANI_OK) { + FI_HILOGE("get undefined value failed, status = %{public}d", status); + return; + } + } + if ((status = env->PromiseResolver_Resolve(deferred, data)) != ANI_OK) { + FI_HILOGE("promiseResolver resolve failed, status = %{public}d", status); + return; + } + return; +} + +ani_status CooperateCommon::ExecAsyncCallBack(ani_env *env, ani_object businessError, + ani_object param, ani_object callbackFunc) +{ + CALL_DEBUG_ENTER; + ani_status status = ANI_ERROR; + ani_ref ani_argv[] = {businessError, param}; + ani_ref ani_result; + ani_class cls; + if ((status = env->FindClass("Lstd/core/Function2;", &cls)) != ANI_OK) { + FI_HILOGE("find calss is failed, status = %{public}d", status); + return status; + } + ani_boolean ret; + env->Object_InstanceOf(callbackFunc, cls, &ret); + if (!ret) { + FI_HILOGE("callbackFunc is not instance Of Function2."); + return status; + } + if ((status = env->FunctionalObject_Call(static_cast(callbackFunc), TWO_PARAMS, + ani_argv, &ani_result)) != ANI_OK) { + FI_HILOGE("call ani func failed, status = %{public}d.", status); + return status; + } + return status; +} + +ani_object CooperateCommon::CreateBooleanObject(ani_env *env, bool value) +{ + CALL_DEBUG_ENTER; + if (env == nullptr) { + FI_HILOGE("Env is null."); + return nullptr; + } + + ani_class persionCls; + ani_status status = ANI_ERROR; + if ((status = env->FindClass("std.core.Boolean", &persionCls)) != ANI_OK) { + FI_HILOGE("Failed to FindClass, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_method personInfoCtor; + if ((status = env->Class_FindMethod(persionCls, "", "z:", &personInfoCtor)) != ANI_OK) { + FI_HILOGE("Failed to FindMethod, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_object personInfoObj; + if ((status = env->Object_New(persionCls, personInfoCtor, &personInfoObj, value)) != ANI_OK) { + FI_HILOGE("Failed to Object_New, status : %{public}d.", static_cast(status)); + return nullptr; + } + return personInfoObj; +} + +ani_object CooperateCommon::CreateIntObject(ani_env *env, int32_t value) +{ + CALL_DEBUG_ENTER; + if (env == nullptr) { + FI_HILOGE("Env is null."); + return nullptr; + } + + ani_class persionCls; + ani_status status = ANI_ERROR; + if ((status = env->FindClass("std.core.Int", &persionCls)) != ANI_OK) { + FI_HILOGE("Failed to FindClass, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_method aniMethod; + if ((status = env->Class_FindMethod(persionCls, "", "i:", &aniMethod)) != ANI_OK) { + FI_HILOGE("Failed to FindMethod, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_object aniObject; + if ((status = env->Object_New(persionCls, aniMethod, &aniObject, value)) != ANI_OK) { + FI_HILOGE("Failed to Object_New, status : %{public}d.", static_cast(status)); + return nullptr; + } + return aniObject; +} + +bool CooperateCommon::GetErrorMsg(int32_t code, std::string &codeMsg) +{ + auto iter = NAPI_ERRORS.find(code); + if (iter == NAPI_ERRORS.end()) { + FI_HILOGE("Error code %{public}d not found", code); + return false; + } + codeMsg = iter->second; + return true; +} + +void CooperateCommon::HandleExecuteResult(int32_t errCode, const std::string param1, + const std::string param2) +{ + switch (errCode) { + case COMMON_PERMISSION_CHECK_ERROR: { + std::string codeMsg; + if (!CooperateCommon::GetErrorMsg(errCode, codeMsg)) { + FI_HILOGE("GetErrorMsg failed"); + return; + } + char buf[300] = { 0 }; + int32_t ret = sprintf_s(buf, sizeof(buf), codeMsg.c_str(), param1.c_str(), param2.c_str()); + if (ret > 0) { + taihe::set_business_error(ret, buf); + } else { + FI_HILOGE("Failed to convert string type to char type, error code:%{public}d", errCode); + } + break; + } + case COMMON_PARAMETER_ERROR: { + taihe::set_business_error(errCode, "param is invalid"); + break; + } + case COMMON_NOT_SYSTEM_APP: { + std::string errMsg; + if (!CooperateCommon::GetErrorMsg(errCode, errMsg)) { + FI_HILOGE("GetErrorMsg failed"); + return; + } + taihe::set_business_error(errCode, errMsg.c_str()); + break; + } + case COMMON_NOT_ALLOWED_DISTRIBUTED: { + std::string errMsg; + if (!CooperateCommon::GetErrorMsg(errCode, errMsg)) { + FI_HILOGE("GetErrorMsg failed"); + return; + } + taihe::set_business_error(COMMON_PARAMETER_ERROR, errMsg.c_str()); + break; + } + default: { + FI_HILOGW("Unknown error throwcode:%{public}d", errCode); + taihe::set_business_error(COMMON_PARAMETER_ERROR, "unknown error"); + break; + } + } +} + +bool CooperateCommon::GetErrMsg(const CoordinationMsgInfo &msgInfo, std::string &msg) +{ + auto iter = COOPERATE_MSG_MAP.find(msgInfo.msg); + if (iter == COOPERATE_MSG_MAP.end()) { + FI_HILOGE("Error code:%{public}d is not founded in COOPERATE_MSG_MAP", static_cast (msgInfo.msg)); + return false; + } + msg = iter->second; + switch (static_cast(msgInfo.errCode)) { + case CoordinationErrCode::COORDINATION_OK: { + msg += "Everything is fine"; + break; + } + case CoordinationErrCode::OPEN_SESSION_FAILED: { + msg += "Open session failed"; + break; + } + case CoordinationErrCode::SEND_PACKET_FAILED: { + msg += "Send packet failed"; + break; + } + case CoordinationErrCode::UNEXPECTED_START_CALL: { + msg += "Unexpected start call"; + break; + } + case CoordinationErrCode::WORKER_THREAD_TIMEOUT: { + msg += "Worker thread timeout"; + break; + } + case CoordinationErrCode::NOT_AOLLOW_COOPERATE_WHEN_MOTION_DRAGGING: { + msg += "Not allow cooperate when motion dragging"; + break; + } + default: + msg +="Softbus bind failed"; + } + return true; +} + +int32_t CooperateCommon::GetErrCode(const CoordinationMsgInfo &msgInfo) +{ + switch (static_cast(msgInfo.errCode)) { + case CoordinationErrCode::OPEN_SESSION_FAILED: { + return CustomErrCode::OPEN_SESSION_FAILED; + } + case CoordinationErrCode::SEND_PACKET_FAILED: { + return CustomErrCode::SEND_PACKET_FAILED; + } + case CoordinationErrCode::UNEXPECTED_START_CALL: { + return CustomErrCode::UNEXPECTED_START_CALL; + } + case CoordinationErrCode::WORKER_THREAD_TIMEOUT: { + return CustomErrCode::WORKER_THREAD_TIMEOUT; + } + case CoordinationErrCode::NOT_AOLLOW_COOPERATE_WHEN_MOTION_DRAGGING: { + return CustomErrCode::NOT_AOLLOW_COOPERATE_WHEN_MOTION_DRAGGING; + } + default: + return msgInfo.errCode; + } +} + +bool CooperateCommon::IsSameHandle(ani_env *env, ani_ref localRef, ani_ref inRef) +{ + ani_boolean isSame = false; + ani_status status; + if ((status = env->Reference_StrictEquals(localRef, inRef, &isSame)) != ANI_OK) { + FI_HILOGE("Failed to Reference_StrictEquals %{public}d", status); + return isSame; + } + return isSame; +} + +/////////////GlobalRefGuard//////////////////////////////////////////////// +GlobalRefGuard::GlobalRefGuard(ani_env *env, ani_object obj): env_(env) +{ + CALL_DEBUG_ENTER; + if (!env_) + return; + if (ANI_OK != env_->GlobalReference_Create(obj, &ref_)) { + FI_HILOGE("Create callback reference failed!"); + ref_ = nullptr; + } +} + +GlobalRefGuard::~GlobalRefGuard() +{ + CALL_DEBUG_ENTER; + if (env_ && ref_) { + FI_HILOGD("Begin ANI Reference Delete!"); + env_->GlobalReference_Delete(ref_); + } +} +} // DeviceStatus +} // Msdp +} // OHOS \ No newline at end of file diff --git a/frameworks/ets/coop/src/ohos.multimodalInput.inputDeviceCooperate.impl.cpp b/frameworks/ets/coop/src/ohos.multimodalInput.inputDeviceCooperate.impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e791c25ea2282bba7d281dee57c481f5d90ea586 --- /dev/null +++ b/frameworks/ets/coop/src/ohos.multimodalInput.inputDeviceCooperate.impl.cpp @@ -0,0 +1,117 @@ +/* + * 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 "ohos.multimodalInput.inputDeviceCooperate.proj.hpp" +#include "ohos.multimodalInput.inputDeviceCooperate.impl.hpp" +#include "taihe/runtime.hpp" +#include "stdexcept" + +#include "fi_log.h" +#include "ani_cooperate_manager.h" + +#undef LOG_TAG +#define LOG_TAG "AniCoopImpl" + +namespace { +using namespace OHOS::Msdp::DeviceStatus; +using namespace OHOS::Msdp; +void EnableAsync(bool enableInput, uintptr_t opq) +{ + CALL_DEBUG_ENTER; + ani_object promise; + ANI_COOPERATE_MGR.Enable(enableInput, opq, promise); + return; +} + +uintptr_t EnablePromise(bool enableInput) +{ + CALL_DEBUG_ENTER; + ani_object promise; + ANI_COOPERATE_MGR.Enable(enableInput, 0, promise); + return reinterpret_cast(promise); +} + +void StartAsync(::taihe::string_view sinkDeviceDescriptor, int32_t srcInputDeviceId, uintptr_t opq) +{ + CALL_DEBUG_ENTER; + ani_object promise; + ANI_COOPERATE_MGR.Start(sinkDeviceDescriptor.c_str(), srcInputDeviceId, opq, promise); +} + +uintptr_t StartPromise(::taihe::string_view sinkDeviceDescriptor, int32_t srcInputDeviceId) +{ + CALL_DEBUG_ENTER; + ani_object promise; + ANI_COOPERATE_MGR.Start(sinkDeviceDescriptor.c_str(), srcInputDeviceId, 0, promise); + return reinterpret_cast(promise); +} + +void StopAsync(uintptr_t opq) +{ + CALL_DEBUG_ENTER; + ani_object promise; + ANI_COOPERATE_MGR.Stop(opq, promise); +} + +uintptr_t StopPromise() +{ + CALL_DEBUG_ENTER; + ani_object promise; + ANI_COOPERATE_MGR.Stop(0, promise); + return reinterpret_cast(promise); +} + +void GetStateAsync(::taihe::string_view deviceDescriptor, uintptr_t opq) +{ + CALL_DEBUG_ENTER; + ani_object promise; + ANI_COOPERATE_MGR.GetState(deviceDescriptor.c_str(), opq, promise); +} + +uintptr_t GetStatePromise(::taihe::string_view deviceDescriptor) +{ + CALL_DEBUG_ENTER; + ani_object promise; + ANI_COOPERATE_MGR.GetState(deviceDescriptor.c_str(), 0, promise); + return reinterpret_cast(promise); +} + +void onCooperation(uintptr_t opq) +{ + CALL_DEBUG_ENTER; + ANI_COOPERATE_MGR.OnCooperation(opq); +} + +void offCooperation(::taihe::optional_view opq) +{ + CALL_DEBUG_ENTER; + ANI_COOPERATE_MGR.OffCooperation(opq); +} +} // namespace + +// Since these macros are auto-generate, lint will cause false positive. +// NOLINTBEGIN +TH_EXPORT_CPP_API_EnableAsync(EnableAsync); +TH_EXPORT_CPP_API_EnablePromise(EnablePromise); +TH_EXPORT_CPP_API_StartAsync(StartAsync); +TH_EXPORT_CPP_API_StartPromise(StartPromise); +TH_EXPORT_CPP_API_StopAsync(StopAsync); +TH_EXPORT_CPP_API_StopPromise(StopPromise); +TH_EXPORT_CPP_API_GetStateAsync(GetStateAsync); +TH_EXPORT_CPP_API_GetStatePromise(GetStatePromise); +TH_EXPORT_CPP_API_onCooperation(onCooperation); +TH_EXPORT_CPP_API_offCooperation(offCooperation); +// NOLINTEND + diff --git a/frameworks/ets/coop/test/test_main.ets b/frameworks/ets/coop/test/test_main.ets new file mode 100644 index 0000000000000000000000000000000000000000..7dede20e1d11aac3bfa83fe7801a9ce6689a73ac --- /dev/null +++ b/frameworks/ets/coop/test/test_main.ets @@ -0,0 +1,180 @@ +/* + * 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. + */ + +import inputDeviceCooperate from '@ohos.multimodalInput.inputDeviceCooperate'; +import { BusinessError, AsyncCallback} from '@ohos.base'; + +const Tag:String = "inputDeviceCooperate" + +const ERR_INVALID_PARAM = 1; +const OVERTIME_QUERY: long = 1000; +const WAIT_OPER: long = 10000; +function wait(delay: long) { + let start = new Date().getTime(); + while (new Date().getTime() - start < delay) { + } +} + +//--- +function test_enable_promise() { + let fun_name: string = 'test_enable_promise'; + console.log(Tag, `into func ${fun_name}`); + try { + inputDeviceCooperate.enable(true).then(() => { + console.log(Tag, `promise ${fun_name} return`); + } ); + wait(OVERTIME_QUERY); + } catch (err) { + console.error(Tag, `${fun_name} catch ${err}`); + } + console.log(Tag, `end func ${fun_name}`); +} + +function test_enable_callback() { + let fun_name: string = 'test_enable_callback'; + console.log(Tag, `into func ${fun_name}`); + let fnCallBack = (err: BusinessError|null, info: undefined) => { + console.log(Tag, `callback ${fun_name} on success`); + }; + try { + inputDeviceCooperate.enable(true, fnCallBack); + wait(OVERTIME_QUERY); + } catch (err) { + console.error(Tag, `${fun_name} catch ${err}`); + } + console.log(Tag, `end func ${fun_name}`); +} + +function test_stop_promise() { + let fun_name: string = 'test_stop_promise'; + console.log(Tag, `into func ${fun_name}`); + try { + inputDeviceCooperate.stop().then(() => { + console.log(Tag, `callback ${fun_name} on success`); + } ); + wait(OVERTIME_QUERY); + } catch (err) { + console.log(Tag, `${fun_name} catch ${err}`); + } + console.log(Tag, `end func ${fun_name}`); +} + +function test_stop_callback() { + let fun_name: string = 'test_stop_callback'; + console.log(Tag, `into func ${fun_name}`); + let fnCallBack = (err: BusinessError|null, info: undefined) => { + console.log(Tag, `callback ${fun_name} on success`); + }; + try { + inputDeviceCooperate.stop(fnCallBack); + wait(OVERTIME_QUERY); + } catch (err) { + console.log(Tag, `${fun_name} catch ${err}`); + } + console.log(Tag, `end func ${fun_name}`); +} + +function test_getstate_callback() { + let fun_name: string = 'test_getstate_callback'; + console.log(Tag, `into func ${fun_name}`); + let fnCallBack = (err: BusinessError|null, info: boolean|undefined) => { + console.log(Tag, `callback ${fun_name} on success, info:${info}`); + }; + try { + let deviceDescriptor = "descriptor"; + inputDeviceCooperate.getState(deviceDescriptor).then((data: boolean) => { + console.log(Tag, `promise ${fun_name} return`); + } ); + wait(OVERTIME_QUERY); + } catch (err) { + console.log(Tag, `${fun_name} catch ${err}`); + } + console.log(Tag + `end func ${fun_name}`); +} + +function test_getstate_promise() { + let fun_name: string = 'test_getstate_promise'; + console.log(Tag, `into func ${fun_name}`); + try { + let deviceDescriptor = "descriptor"; + inputDeviceCooperate.getState(deviceDescriptor).then((data: boolean) => { + console.log(Tag, `promise ${fun_name} return`); + } ); + wait(OVERTIME_QUERY); + } catch (err) { + console.log(Tag, `${fun_name} catch ${err}`); + } + console.log(Tag + `end func ${fun_name}`); +} + +function test_start_callback() { + let fun_name: string = 'test_start_callback'; + console.log(Tag, `into func ${fun_name}`); + try { + let sinkDeviceDescriptor = "descriptor"; + let srcInputDeviceId: int = 0; + let fnCallBack = (err: BusinessError|null, info: undefined) => { + console.log(Tag, `callback ${fun_name} on success`); + }; + inputDeviceCooperate.start(sinkDeviceDescriptor, srcInputDeviceId, fnCallBack); + wait(OVERTIME_QUERY); + } catch (err) { + console.log(Tag, `${fun_name} catch error: ${err}` ); + } + console.log(Tag, `${fun_name} end`); +} + + +function test_start_promise() { + let fun_name: string = 'test_start_promise'; + console.log(Tag, `into func ${fun_name}`); + try { + let sinkDeviceDescriptor = "descriptor"; + let srcInputDeviceId: int = 0; + let fnCallBack = () => { + console.log(Tag, `Monitor ${fun_name} on success`); + }; + inputDeviceCooperate.start(sinkDeviceDescriptor, srcInputDeviceId).then(fnCallBack); + wait(OVERTIME_QUERY); + } catch (err) { + console.log(Tag, `${fun_name} catch error:${err}`); + } + console.log(Tag, `${fun_name} end`); +} + +function test_on_off_change() { + let fun_name: string = 'test_on_off_change'; + console.log(Tag, `into func on ${fun_name}`); + try { + let fnCallBack = (err: BusinessError|null, info: inputDeviceCooperate.Coopinfo | undefined) => { + console.log(Tag, `Monitor ${fun_name} on success`); + console.log(Tag, `Monitor ${fun_name} on success ${JSON.stringify(info)}`); + }; + inputDeviceCooperate.on('cooperation', fnCallBack); + console.log(Tag, `${fun_name} begin wait`); + wait(WAIT_OPER); + console.log(Tag, `${fun_name} end wait`); + inputDeviceCooperate.off('cooperation', fnCallBack); + } catch (err) { + console.log(Tag, `${fun_name} catch error:`, err); + } + console.log(Tag, `end func on ${fun_name}`); +} + +function main() { + console.println(Tag + ` into main`); + + console.println(Tag + ` out main`); +} \ No newline at end of file