diff --git a/README.md b/README.md index 2246be83411e4b83a928f6daa67ba6df7f59b7c6..c28be8dbc1c6d3860201df5317b620e1fbb9e240 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,86 @@ -# systemabilitymgr_selectionfwk +# selectionfwk #### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +划词服务,通过剪贴板获取划词内存,提供划词配置同步接口,划词服务SA监听多模键鼠事件获取鼠标和触控板操作窗口和应用信息,DFX增强。 -#### 软件架构 -软件架构说明 +#### 仓路径 +/foundation/systemabilitymgr/selectionfwk +## 目录 -#### 安装教程 +``` +/foundation/systemabilitymgr +├── selectionfwk +│ ├── common # 公共代码 +│ ├── etc # 组件包含的进程的配置文件 +│ ├── frameworks # 接口实现 +│ │ └── js/napi # 划词框架napi接口 +│ │ └── native # native接口 +│ ├── sa_profile # sa定义 +│ ├── services # 划词框架服务 +│ ├── test # 接口测试目录 +│ │ └── unitest # 接口的单元测试 +│ ├── utils # 核心服务工具代码目录 +``` -1. xxxx -2. xxxx -3. xxxx +### 编译步骤 -#### 使用说明 +- 全量编译 -1. xxxx -2. xxxx -3. xxxx +修改build.gn文件后编译命令 +``` +$ ./build.sh --product-name rk3568 --ccache +``` +未修改build.gn文件编译命令 +``` +$ ./build.sh --product-name rk3568 --ccache --fast-rebuild +``` -#### 参与贡献 +- 单独编译 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +``` +$ ./build.sh --product-name rk3568 --ccache --build-target selectionfwk +``` + +### 测试步骤 + +1. 测试方法 +查看服务进程 +``` +# ps -ef | grep selection +``` +启动划词服务 +``` +# param set sys.selection.switch on +``` +关闭划词服务 +``` +# param set sys.selection.switch off +``` +切换划词应用 +``` +# param set sys.selection.app com.selection.selectionapplication/SelectionExtensionAbility +``` +设置划词触发方式 +``` +# param set sys.selection.trigger "" +``` -#### 特技 +2. 获取日志命令 -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +打开debug日志 +``` +# hilog -b D +``` +过滤日志 +``` +# hilog -T SELECTION_SERVICE +``` + +## 参与贡献 + +1. Fork 本仓库 +2. 提交代码 +3. 新建 Pull Request +4. commit完成即可 \ No newline at end of file diff --git a/bundle.json b/bundle.json new file mode 100644 index 0000000000000000000000000000000000000000..7583f054a04dd6d30a789c3d823db78669ed027e --- /dev/null +++ b/bundle.json @@ -0,0 +1,92 @@ +{ + "name": "@ohos/selectionfwk", + "description": "Provide word selection capabilities", + "version": "1.0", + "license": "Apache License 2.0", + "segment": { + "destPath": "foundation/systemabilitymgr/selectionfwk" + }, + "component": { + "name": "selectionfwk", + "subsystem": "systemabilitymgr", + "syscap": [ + "SystemCapability.SelectionInput.Selection" + ], + "features": [], + "adapted_system_type": [ + "mini", + "small", + "standard" + ], + "rom": "5831KB", + "ram": "5831KB", + "deps": { + "components": [ + "c_utils", + "eventhandler", + "ipc", + "safwk", + "hilog", + "hitrace", + "samgr", + "init", + "input", + "napi", + "ability_base", + "ability_runtime", + "access_token", + "window_manager", + "pasteboard", + "relational_store", + "resource_management", + "graphic_2d", + "bundle_framework", + "ffrt", + "config_policy", + "os_account", + "cJSON", + "common_event_service", + "hicollie", + "hisysevent", + "memmgr", + "resource_schedule_service", + "hiappevent" + ], + "third_party": [ + ] + }, + "build": { + "sub_component": [ + "//foundation/systemabilitymgr/selectionfwk/common:selection_common", + "//foundation/systemabilitymgr/selectionfwk/etc/init:selection_service_cfg", + "//foundation/systemabilitymgr/selectionfwk/etc/para:selection_para", + "//foundation/systemabilitymgr/selectionfwk/etc/para:selection_para_dac", + "//foundation/systemabilitymgr/selectionfwk/service:selection_service", + "//foundation/systemabilitymgr/selectionfwk/sa_profile:selection_service_sa_profile", + "//foundation/systemabilitymgr/selectionfwk/frameworks/js/napi/selection_panel:selectionpanel_napi", + "//foundation/systemabilitymgr/selectionfwk/frameworks/js/napi/selection_extension_ability:selectionextensionability_napi", + "//foundation/systemabilitymgr/selectionfwk/frameworks/js/napi/selection_extension_context:selectionextensioncontext_napi", + "//foundation/systemabilitymgr/selectionfwk/frameworks/native/selection_extension:selection_extension_ability_native", + "//foundation/systemabilitymgr/selectionfwk/frameworks/js/napi/selection_ability:selectionmanager_napi", + "//foundation/systemabilitymgr/selectionfwk/frameworks/native/selection_ability:selection_ability", + "//foundation/systemabilitymgr/selectionfwk/interfaces/inner_kits/selection_client:selection_client", + "//foundation/systemabilitymgr/selectionfwk/hiappevent_agent:selection_hiappevent_agent" + ], + "inner_kits": [ + { + "name": "//foundation/systemabilitymgr/selectionfwk/interfaces/inner_kits/selection_client:selection_client", + "header": { + "header_files": [ + "selection_client.h" + ], + "header_base": "//foundation/systemabilitymgr/selectionfwk/interfaces/inner_kits/selection_client/include" + } + } + ], + "test": [ + "//foundation/systemabilitymgr/selectionfwk/test/unittest:selection_manager_ut", + "//foundation/systemabilitymgr/selectionfwk/test/fuzztest:selection_service_fuzztest" + ] + } + } +} diff --git a/common/BUILD.gn b/common/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..1ea8add0dcd1e63fcc2d46471935f7fd425a2d56 --- /dev/null +++ b/common/BUILD.gn @@ -0,0 +1,68 @@ +# 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("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") +import("//build/ohos.gni") + +config("selection_js_common_public_config") { + visibility = [ "./*" ] + include_dirs = [ + "./", + ] +} + +ohos_static_library("selection_common") { + branch_protector_ret = "pac_ret" + + include_dirs = [ + "./", + "../utils/include", + ] + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + sources = [ + "event_checker.cpp", + "callback_handler.cpp", + "callback_object.cpp", + "util.cpp", + "selectionmethod_trace.cpp", + "selectionfwk_js_utils.cpp", + ] + + ldflags = [ "-Wl,--exclude-libs=ALL" ] + cflags = [ + "-fdata-sections", + "-ffunction-sections", + "-fvisibility=hidden", + "-Wno-c99-designator", + ] + public_configs = [ ":selection_js_common_public_config" ] + deps = [] + external_deps = [ + "eventhandler:libeventhandler", + "napi:ace_napi", + "hilog:libhilog", + "hitrace:hitrace_meter", + "hitrace:libhitracechain", + "ability_base:want", + "ability_runtime:abilitykit_native", + "ability_runtime:extensionkit_native", + "ffrt:libffrt", + ] + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} diff --git a/common/block_data.h b/common/block_data.h new file mode 100644 index 0000000000000000000000000000000000000000..d857a2f0a87b4de4cf9d856d1903c7a98d6b59bd --- /dev/null +++ b/common/block_data.h @@ -0,0 +1,75 @@ +/* + * 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 OHOS_BLOCK_DATA_H +#define OHOS_BLOCK_DATA_H +#include +#include + +namespace OHOS { +namespace SelectionFwk { +template +class BlockData { +public: + explicit BlockData(uint32_t interval, const T &invalid = T()) : INTERVAL(interval), data_(invalid) + { + } + + ~BlockData() + { + } + +public: + void SetValue(const T &data) + { + std::lock_guard lock(mutex_); + data_ = data; + isSet_ = true; + cv_.notify_one(); + } + + T GetValue() + { + std::unique_lock lock(mutex_); + cv_.wait_for(lock, std::chrono::milliseconds(INTERVAL), [this]() { return isSet_; }); + T data = data_; + return data; + } + + bool GetValue(T &data) + { + std::unique_lock lock(mutex_); + cv_.wait_for(lock, std::chrono::milliseconds(INTERVAL), [this]() { return isSet_; }); + data = data_; + return isSet_; + } + + void Clear(const T &invalid = T()) + { + std::lock_guard lock(mutex_); + isSet_ = false; + data_ = invalid; + } + +private: + bool isSet_ = false; + const uint32_t INTERVAL; + T data_; + std::mutex mutex_; + std::condition_variable cv_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // OHOS_BLOCK_DATA_H diff --git a/common/callback_handler.cpp b/common/callback_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4b3cde038a43f7fc6dbc50d542cec7ceed9f90c --- /dev/null +++ b/common/callback_handler.cpp @@ -0,0 +1,51 @@ +/* + * 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 "callback_handler.h" + +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { +constexpr size_t MAX_ARGV_COUNT = 10; +void JsCallbackHandler::Execute(const std::shared_ptr &object, const ArgContainer &argContainer, + napi_value &output) +{ + if (object->threadId_ != std::this_thread::get_id()) { + SELECTION_HILOGW("threadId not same!"); + return; + } + napi_value argv[MAX_ARGV_COUNT] = { nullptr }; + if (argContainer.argvProvider != nullptr && !argContainer.argvProvider(object->env_, argv, MAX_ARGV_COUNT)) { + return; + } + napi_value callback = nullptr; + napi_value global = nullptr; + napi_get_reference_value(object->env_, object->callback_, &callback); + if (callback == nullptr) { + SELECTION_HILOGE("callback is nullptr!"); + return; + } + napi_get_global(object->env_, &global); + SelectionMethodSyncTrace tracer("Execute napi_call_function"); + auto status = napi_call_function(object->env_, global, callback, argContainer.argc, argv, &output); + if (status != napi_ok) { + output = nullptr; + SELECTION_HILOGE("napi_call_function is failed!"); + return; + } +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/common/callback_handler.h b/common/callback_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..d5b55ef287de35e6e9d9e545b77ebe616630f39d --- /dev/null +++ b/common/callback_handler.h @@ -0,0 +1,72 @@ +/* + * 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 OHOS_SELECTION_CALLBACK_HANDLER_H +#define OHOS_SELECTION_CALLBACK_HANDLER_H + +#include +#include "selectionmethod_trace.h" +#include "callback_object.h" +#include "util.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" + +namespace OHOS { +namespace SelectionFwk { +class JsCallbackHandler { +public: + using ArgvProvider = std::function; + struct ArgContainer { + size_t argc{ 0 }; + ArgvProvider argvProvider{ nullptr }; + }; + // 0 means the callback has no param. + static void Traverse(const std::vector> &objects, + const ArgContainer &argContainer = { 0, nullptr }) + { + SelectionMethodSyncTrace tracer("Traverse callback"); + for (const auto &object : objects) { + if (object == nullptr) { + continue; + } + JsUtil::ScopeGuard scopeGuard(object->env_); + napi_value jsOutput = nullptr; + Execute(object, argContainer, jsOutput); + } + } + template + static void Traverse(const std::vector> &objects, + const ArgContainer &argContainer, T &output) + { + SelectionMethodSyncTrace tracer("Traverse callback with output"); + for (const auto &object : objects) { + if (object == nullptr) { + break; + } + JsUtil::ScopeGuard scopeGuard(object->env_); + napi_value jsOutput = nullptr; + Execute(object, argContainer, jsOutput); + if (jsOutput != nullptr && JsUtil::GetValue(object->env_, jsOutput, output)) { + break; + } + } + } + +private: + static void Execute(const std::shared_ptr &object, const ArgContainer &argContainer, + napi_value &output); +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // OHOS_SELECTION_CALLBACK_HANDLER_H diff --git a/common/callback_object.cpp b/common/callback_object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d1f340d34389468b618c99ad09986e283a62322 --- /dev/null +++ b/common/callback_object.cpp @@ -0,0 +1,112 @@ +/* + * 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 "callback_object.h" +#include "selection_log.h" + +#include + +namespace OHOS { +namespace SelectionFwk { +constexpr int32_t MAX_TIMEOUT = 2000; +JSCallbackObject::JSCallbackObject(napi_env env, napi_value callback, std::thread::id threadId, + std::shared_ptr jsHandler) + : env_(env), threadId_(threadId), jsHandler_(jsHandler) +{ + napi_create_reference(env, callback, 1, &callback_); +} + +JSCallbackObject::~JSCallbackObject() +{ + if (callback_ != nullptr) { + if (threadId_ == std::this_thread::get_id()) { + napi_delete_reference(env_, callback_); + env_ = nullptr; + return; + } + isDone_ = std::make_shared>(MAX_TIMEOUT, false); + std::string type = "~JSCallbackObject"; + auto eventHandler = jsHandler_; + if (eventHandler == nullptr) { + SELECTION_HILOGE("eventHandler is nullptr!"); + return; + } + auto task = [env = env_, callback = callback_, isDone = isDone_]() { + napi_delete_reference(env, callback); + bool isFinish = true; + isDone->SetValue(isFinish); + }; + eventHandler->PostTask(task, type); + isDone_->GetValue(); + } + env_ = nullptr; +} + +bool JSCallbackObject::operator==(const JSCallbackObject& other) const +{ + if (other.env_ != env_ || other.threadId_ != threadId_) { + return false; + } + + napi_value thisValue; + napi_value otherValue; + napi_status status = napi_get_reference_value(env_, this->callback_, &thisValue); + if (status != napi_ok) { + return false; + } + status = napi_get_reference_value(env_, other.callback_, &otherValue); + if (status != napi_ok) { + return false; + } + + bool result; + status = napi_strict_equals(env_, thisValue, otherValue, &result); + if (status != napi_ok) { + return false; + } + + return result; +} + +JSMsgHandlerCallbackObject::JSMsgHandlerCallbackObject(napi_env env, napi_value onTerminated, napi_value onMessage) + : env_(env), handler_(AppExecFwk::EventHandler::Current()), threadId_(std::this_thread::get_id()) +{ + napi_create_reference(env, onTerminated, 1, &onTerminatedCallback_); + napi_create_reference(env, onMessage, 1, &onMessageCallback_); +} + +JSMsgHandlerCallbackObject::~JSMsgHandlerCallbackObject() +{ + if (threadId_ == std::this_thread::get_id()) { + if (onTerminatedCallback_ != nullptr) { + napi_delete_reference(env_, onTerminatedCallback_); + } + if (onMessageCallback_ != nullptr) { + napi_delete_reference(env_, onMessageCallback_); + } + env_ = nullptr; + return; + } + SELECTION_HILOGW("Thread id is not same, abstract destructor is run in muti-thread!"); + env_ = nullptr; +} + +std::shared_ptr JSMsgHandlerCallbackObject::GetEventHandler() +{ + std::lock_guard lock(eventHandlerMutex_); + return handler_; +} +} // namespace SelectionFwk +} // namespace OHOS diff --git a/common/callback_object.h b/common/callback_object.h new file mode 100644 index 0000000000000000000000000000000000000000..a6dfdd76a399d595e808482146413c1841c6301d --- /dev/null +++ b/common/callback_object.h @@ -0,0 +1,59 @@ +/* + * 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 OHOS_CALLBACK_OBJECT_H +#define OHOS_CALLBACK_OBJECT_H + +#include +#include + +#include "block_data.h" +#include "event_handler.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" + +namespace OHOS { +namespace SelectionFwk { +class JSCallbackObject { +public: + JSCallbackObject(napi_env env, napi_value callback, std::thread::id threadId, + std::shared_ptr jsHandler); + ~JSCallbackObject(); + bool operator==(const JSCallbackObject& other) const; + + napi_ref callback_ = nullptr; + napi_env env_{}; + std::thread::id threadId_; + std::shared_ptr> isDone_; + std::shared_ptr jsHandler_; +}; + +// Ensure this object abstract in constract thread. +class JSMsgHandlerCallbackObject { +public: + JSMsgHandlerCallbackObject(napi_env env, napi_value onTerminated, napi_value onMessage); + ~JSMsgHandlerCallbackObject(); + napi_env env_{}; + napi_ref onTerminatedCallback_ = nullptr; + napi_ref onMessageCallback_ = nullptr; + std::shared_ptr GetEventHandler(); + +private: + std::mutex eventHandlerMutex_; + std::shared_ptr handler_ = nullptr; + std::thread::id threadId_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // OHOS_CALLBACK_OBJECT_H diff --git a/common/concurrent_map.h b/common/concurrent_map.h new file mode 100644 index 0000000000000000000000000000000000000000..986b01a7ee36ef99a741e9ab6c05620931b984aa --- /dev/null +++ b/common/concurrent_map.h @@ -0,0 +1,302 @@ +/* + * 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 OHOS_SELECTION_FRAMEWORKS_COMMON_CONCURRENT_MAP_H +#define OHOS_SELECTION_FRAMEWORKS_COMMON_CONCURRENT_MAP_H +#include +#include +#include +namespace OHOS { +template +class ConcurrentMap { + template + static _First First(); + +public: + using map_type = typename std::map<_Key, _Tp>; + using filter_type = typename std::function; + using key_type = typename std::map<_Key, _Tp>::key_type; + using mapped_type = typename std::map<_Key, _Tp>::mapped_type; + using value_type = typename std::map<_Key, _Tp>::value_type; + using size_type = typename std::map<_Key, _Tp>::size_type; + using reference = typename std::map<_Key, _Tp>::reference; + using const_reference = typename std::map<_Key, _Tp>::const_reference; + + ConcurrentMap() = default; + ~ConcurrentMap() + { + Clear(); + } + + ConcurrentMap(const ConcurrentMap &other) + { + operator=(std::move(other)); + } + + ConcurrentMap &operator=(const ConcurrentMap &other) noexcept + { + if (this == &other) { + return *this; + } + auto tmp = other.Clone(); + std::lock_guard lock(mutex_); + entries_ = std::move(tmp); + return *this; + } + + ConcurrentMap(ConcurrentMap &&other) noexcept + { + operator=(std::move(other)); + } + + ConcurrentMap &operator=(ConcurrentMap &&other) noexcept + { + if (this == &other) { + return *this; + } + auto tmp = other.Steal(); + std::lock_guard lock(mutex_); + entries_ = std::move(tmp); + return *this; + } + + bool Emplace() noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.emplace(); + return it.second; + } + + template + typename std::enable_if()), filter_type>, bool>::type Emplace( + _Args &&...args) noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.emplace(std::forward<_Args>(args)...); + return it.second; + } + + template + typename std::enable_if, bool>::type Emplace(const _Filter &filter, + _Args &&...args) noexcept + { + std::lock_guard lock(mutex_); + if (!filter(entries_)) { + return false; + } + auto it = entries_.emplace(std::forward<_Args>(args)...); + return it.second; + } + + std::pair Find(const key_type &key) const noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it == entries_.end()) { + return std::pair{ false, mapped_type() }; + } + + return std::pair{ true, it->second }; + } + + bool Contains(const key_type &key) const noexcept + { + std::lock_guard lock(mutex_); + return (entries_.find(key) != entries_.end()); + } + + template + bool InsertOrAssign(const key_type &key, _Obj &&obj) noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.insert_or_assign(key, std::forward<_Obj>(obj)); + return it.second; + } + + bool Insert(const key_type &key, const mapped_type &value) noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.insert(value_type{ key, value }); + return it.second; + } + + size_type Erase(const key_type &key) noexcept + { + std::lock_guard lock(mutex_); + return entries_.erase(key); + } + + void Clear() noexcept + { + std::lock_guard lock(mutex_); + return entries_.clear(); + } + + bool Empty() const noexcept + { + std::lock_guard lock(mutex_); + return entries_.empty(); + } + + size_type Size() const noexcept + { + std::lock_guard lock(mutex_); + return entries_.size(); + } + + // The action`s return true means meeting the erase condition + // The action`s return false means not meeting the erase condition + size_type EraseIf(const std::function &action) noexcept + { + if (action == nullptr) { + return 0; + } + std::lock_guard lock(mutex_); +#if __cplusplus > 201703L + auto count = std::erase_if(entries_, + [&action](value_type &value) -> bool { return action(value.first, value.second); }); +#else + auto count = entries_.size(); + for (auto it = entries_.begin(); it != entries_.end();) { + if (action((*it).first, (*it).second)) { + it = entries_.erase(it); + } else { + ++it; + } + } + count -= entries_.size(); +#endif + return count; + } + + void ForEach(const std::function &action) + { + if (action == nullptr) { + return; + } + std::lock_guard lock(mutex_); + for (auto &[key, value] : entries_) { + if (action(key, value)) { + break; + } + } + } + + void ForEachCopies(const std::function &action) + { + if (action == nullptr) { + return; + } + auto entries = Clone(); + for (auto &[key, value] : entries) { + if (action(key, value)) { + break; + } + } + } + + // The action's return value means that the element is keep in map or not; true means keeping, false means removing. + bool Compute(const key_type &key, const std::function &action) + { + if (action == nullptr) { + return false; + } + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it == entries_.end()) { + auto result = entries_.emplace(key, mapped_type()); + it = result.second ? result.first : entries_.end(); + } + if (it == entries_.end()) { + return false; + } + if (!action(it->first, it->second)) { + entries_.erase(key); + } + return true; + } + + // The action's return value means that the element is keep in map or not; true means keeping, false means removing. + bool ComputeIfPresent(const key_type &key, const std::function &action) + { + if (action == nullptr) { + return false; + } + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it == entries_.end()) { + return false; + } + if (!action(key, it->second)) { + entries_.erase(key); + } + return true; + } + + bool ComputeIfAbsent(const key_type &key, const std::function &action) + { + if (action == nullptr) { + return false; + } + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it != entries_.end()) { + return false; + } + entries_.emplace(key, action(key)); + return true; + } + + bool ComputeIfAbsent(const key_type &key, const std::function &action) + { + if (action == nullptr) { + return false; + } + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it != entries_.end()) { + return false; + } + auto result = entries_.emplace(key, mapped_type()); + it = result.second ? result.first : entries_.end(); + if (it == entries_.end()) { + return false; + } + if (!action(it->first, it->second)) { + entries_.erase(key); + return false; + } + return true; + } + +private: + std::map<_Key, _Tp> Steal() noexcept + { + std::lock_guard lock(mutex_); + return std::move(entries_); + } + + std::map<_Key, _Tp> Clone() const noexcept + { + std::lock_guard lock(mutex_); + return entries_; + } + +private: + mutable std::recursive_mutex mutex_; + std::map<_Key, _Tp> entries_; +}; +} // namespace OHOS +#endif // OHOS_SELECTION_FRAMEWORKS_COMMON_CONCURRENT_MAP_H diff --git a/common/event_checker.cpp b/common/event_checker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1693040fab9172e0a9dc96438ef3eb5b22e99944 --- /dev/null +++ b/common/event_checker.cpp @@ -0,0 +1,34 @@ +/* + * 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_checker.h" +#include + +namespace OHOS { +namespace SelectionFwk { +const std::unordered_set EVENT_TYPES[static_cast(EventSubscribeModule::MODULE_END)] = { + [static_cast(EventSubscribeModule::SELECTION_METHOD_ABILITY)] = { "selectionCompleted" }, + [static_cast(EventSubscribeModule::PANEL)] = { "destroyed", "hidden" } +}; + +bool EventChecker::IsValidEventType(EventSubscribeModule module, const std::string &type) +{ + if (module < EventSubscribeModule::MODULE_BEGIN || module >= EventSubscribeModule::MODULE_END) { + return false; + } + return EVENT_TYPES[static_cast(module)].find(type) != EVENT_TYPES[static_cast(module)].end(); +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/common/event_checker.h b/common/event_checker.h new file mode 100644 index 0000000000000000000000000000000000000000..82113c3159d5ee5ca6ef57d0abee2eeb69ef3868 --- /dev/null +++ b/common/event_checker.h @@ -0,0 +1,34 @@ +/* + * 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 OHOS_EVENT_CHECK_H +#define OHOS_EVENT_CHECK_H +#include + +namespace OHOS { +namespace SelectionFwk { +enum class EventSubscribeModule : uint32_t { + MODULE_BEGIN = 0, + SELECTION_METHOD_ABILITY, + PANEL, + MODULE_END, +}; +class EventChecker { +public: + static bool IsValidEventType(EventSubscribeModule module, const std::string &type); +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // OHOS_EVENT_CHECK_H diff --git a/common/ffrt_block_queue.h b/common/ffrt_block_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..ae90f51d80e95146bc33327728990228c2162d10 --- /dev/null +++ b/common/ffrt_block_queue.h @@ -0,0 +1,70 @@ +/* + * 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 OHOS_INPUTMETHOD_FFRT_BLOCK_QUEUE_H +#define OHOS_INPUTMETHOD_FFRT_BLOCK_QUEUE_H +#include + +#include "cpp/mutex.h" +#include "ffrt.h" + +namespace OHOS { +namespace SelectionFwk { +template class FFRTBlockQueue { +public: + explicit FFRTBlockQueue(uint32_t timeout) : timeout_(timeout) + { + } + + ~FFRTBlockQueue() = default; + + void Pop() + { + std::unique_lock lock(queuesMutex_); + queues_.pop(); + cv_.notify_all(); + } + + void Push(const T &data) + { + std::unique_lock lock(queuesMutex_); + queues_.push(data); + } + + void Wait(const T &data) + { + std::unique_lock lock(queuesMutex_); + cv_.wait_for(lock, std::chrono::milliseconds(timeout_), [&data, this]() { return data == queues_.front(); }); + } + + bool GetFront(T &data) + { + std::unique_lock lock(queuesMutex_); + if (queues_.empty()) { + return false; + } + data = queues_.front(); + return true; + } + +private: + const uint32_t timeout_; + ffrt::mutex queuesMutex_; + std::queue queues_; + ffrt::condition_variable cv_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // OHOS_INPUTMETHOD_FFRT_BLOCK_QUEUE_H diff --git a/common/selection_data_inner.h b/common/selection_data_inner.h new file mode 100644 index 0000000000000000000000000000000000000000..47d743818f296064ce2cd8d6823cbdb59c393e6c --- /dev/null +++ b/common/selection_data_inner.h @@ -0,0 +1,179 @@ +/* + * 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 SELECTION_DATA_INNER +#define SELECTION_DATA_INNER + +#include +#include +#include +#include +#include +#include "parcel.h" +#include "selection_interface.h" + +namespace OHOS { +namespace SelectionFwk { + +struct SelectionInfoData : public Parcelable { + SelectionInfo data; + + bool ReadFromParcel(Parcel &in) { + data.selectionType = static_cast(in.ReadInt8()); + data.text = in.ReadString(); + data.startDisplayX = in.ReadInt32(); + data.startDisplayY = in.ReadInt32(); + data.endDisplayX = in.ReadInt32(); + data.endDisplayY = in.ReadInt32(); + data.startWindowX = in.ReadInt32(); + data.startWindowY = in.ReadInt32(); + data.endWindowX = in.ReadInt32(); + data.endWindowY = in.ReadInt32(); + data.displayId = in.ReadUint32(); + data.windowId = in.ReadUint32(); + data.bundleName = in.ReadString(); + return true; + } + + bool Marshalling(Parcel &out) const { + if (!out.WriteInt8(static_cast(data.selectionType))) { + return false; + } + if (!out.WriteString(data.text)) { + return false; + } + if (!out.WriteInt32(data.startDisplayX)) { + return false; + } + if (!out.WriteInt32(data.startDisplayY)) { + return false; + } + if (!out.WriteInt32(data.endDisplayX)) { + return false; + } + if (!out.WriteInt32(data.endDisplayY)) { + return false; + } + if (!out.WriteInt32(data.startWindowX)) { + return false; + } + if (!out.WriteInt32(data.startWindowY)) { + return false; + } + if (!out.WriteInt32(data.endWindowX)) { + return false; + } + if (!out.WriteInt32(data.endWindowY)) { + return false; + } + if (!out.WriteUint32(data.displayId)) { + return false; + } + if (!out.WriteUint32(data.windowId)) { + return false; + } + if (!out.WriteString(data.bundleName)) { + return false; + } + return true; + } + + static SelectionInfoData *Unmarshalling(Parcel &in) { + SelectionInfoData *data = new (std::nothrow) SelectionInfoData(); + if (data && !data->ReadFromParcel(in)) { + delete data; + data = nullptr; + } + return data; + } + + std::string ToString() const { + std::ostringstream oss; + oss << "SelectionInfo { selectionType: " << data.selectionType << + ", text.length: " << data.text.length() << + ", startDisplayX: " << data.startDisplayX << + ", startDisplayY: " << data.startDisplayY << + ", endDisplayX: " << data.endDisplayX << + ", endDisplayY: " << data.endDisplayY << + ", startWindowX: " << data.startWindowX << + ", startWindowY: " << data.startWindowY << + ", endWindowX: " << data.endWindowX << + ", endWindowY: " << data.endWindowY << + ", displayId: " << data.displayId << + ", windowId: " << data.windowId << + ", bundleName: " << data.bundleName << "}"; + return oss.str(); + } +}; + +enum class FocusChangeSource : uint32_t { + WindowManager, + InputManager, +}; + +class SelectionFocusChangeInfo : public Parcelable { +public: + SelectionFocusChangeInfo() = default; + SelectionFocusChangeInfo(uint32_t winId, uint64_t displayId, int32_t pid, int32_t uid, uint32_t type, + bool isFocused, FocusChangeSource source): windowId_(winId), displayId_(displayId), pid_(pid), uid_(uid), + windowType_(type), isFocused_(isFocused), source_(source) {}; + + ~SelectionFocusChangeInfo() = default; + + virtual bool Marshalling(Parcel& parcel) const + { + bool ret = parcel.WriteInt32(windowId_) && parcel.WriteUint64(displayId_) && + parcel.WriteInt32(pid_) && parcel.WriteInt32(uid_) && + parcel.WriteUint32(static_cast(windowType_)) && + parcel.WriteBool(isFocused_) && + parcel.WriteUint32(static_cast(source_)); + return ret; + } + + static SelectionFocusChangeInfo* Unmarshalling(Parcel& parcel) + { + auto focusChangeInfo = new SelectionFocusChangeInfo(); + bool res = parcel.ReadInt32(focusChangeInfo->windowId_) && parcel.ReadUint64(focusChangeInfo->displayId_) && + parcel.ReadInt32(focusChangeInfo->pid_) && parcel.ReadInt32(focusChangeInfo->uid_) && + parcel.ReadUint32(focusChangeInfo->windowType_) && + parcel.ReadBool(focusChangeInfo->isFocused_); + if (!res) { + delete focusChangeInfo; + return nullptr; + } + focusChangeInfo->source_ = static_cast(parcel.ReadUint32()); + return focusChangeInfo; + } + + std::string ToString() const + { + std::ostringstream oss; + oss << "SelectionFocusChangeInfo { windowId: " << windowId_ << ", displayId: " << displayId_ << + ", windowType: " << windowType_ << ", isFocused: " << isFocused_ << + ", source_: " << static_cast(source_) << "}"; + return oss.str(); + } + + int32_t windowId_ = -1; + uint64_t displayId_ = 0; + int32_t pid_ = -1; + int32_t uid_ = -1; + uint32_t windowType_ = 1; + bool isFocused_ = false; + FocusChangeSource source_ = FocusChangeSource::WindowManager; +}; +} +} + +#endif \ No newline at end of file diff --git a/common/selection_interface.h b/common/selection_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..70444e7258d3e9fc6dc1ff31cb1cfeb77eee7c45 --- /dev/null +++ b/common/selection_interface.h @@ -0,0 +1,56 @@ +/* + * 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 SELECTION_INTERFACE_H +#define SELECTION_INTERFACE_H + +#include +#include +#include +#include + +namespace OHOS { +namespace SelectionFwk { + +typedef enum { + MOVE_SELECTION = 1, + DOUBLE_CLICKED_SELECTION = 2, + TRIPLE_CLICKED_SELECTION = 3, +} SelectionType; + +struct SelectionInfo { + SelectionType selectionType; + std::string text = ""; + int32_t startDisplayX = 0; + int32_t startDisplayY = 0; + int32_t endDisplayX = 0; + int32_t endDisplayY = 0; + int32_t startWindowX = 0; + int32_t startWindowY = 0; + int32_t endWindowX = 0; + int32_t endWindowY = 0; + uint32_t displayId = 0; + uint32_t windowId = 0; + std::string bundleName = ""; +}; + +class SelectionInterface { +public: + virtual ~SelectionInterface() = default; + virtual int32_t OnSelectionEvent(const SelectionInfo &selectionInfo) = 0; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // SELECTION_INTERFACE_H \ No newline at end of file diff --git a/common/selection_js_utils.h b/common/selection_js_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..379504a93c9d9df508b9564969f16372be596663 --- /dev/null +++ b/common/selection_js_utils.h @@ -0,0 +1,172 @@ +/* + * 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 SELECTAIONFWK_JS_UTILS_H +#define SELECTAIONFWK_JS_UTILS_H + +#include + +#include "ability.h" +#include "selection_log.h" +#include "callback_object.h" +#include "util.h" +#include "napi/native_api.h" +#include "napi/native_common.h" +#include "napi/native_node_api.h" +#include "string_ex.h" + +using Ability = OHOS::AppExecFwk::Ability; +namespace OHOS { +namespace SelectionFwk { +enum SFErrorCode : int32_t { + EXCEPTION_SUCCESS = 0, + EXCEPTION_PARAMCHECK = 401, + EXCEPTION_SELECTION_SERVICE = 33600001, + EXCEPTION_PANEL_DESTROYED = 33600002, + EXCEPTION_INVALID_OPERATION = 33600003, +}; + +enum TypeCode : int32_t { + TYPE_NONE = 0, + TYPE_UNDEFINED, + TYPE_NULL, + TYPE_BOOLEAN, + TYPE_NUMBER, + TYPE_STRING, + TYPE_SYMBOL, + TYPE_OBJECT, + TYPE_FUNCTION, + TYPE_EXTERNAL, + TYPE_BIGINT, + TYPE_ARRAY_BUFFER, + TYPE_ARRAY, +}; + +/* check condition, return and logging if condition not true. */ +#define PARAM_CHECK_RETURN(env, condition, message, typeCode, retVal) \ + do { \ + if (!(condition)) { \ + JsUtils::ThrowException(env, SFErrorCode::EXCEPTION_PARAMCHECK, message, typeCode); \ + return retVal; \ + } \ + } while (0) + +#define PARAM_CHECK_RETURN_VOID(env, condition, message, typeCode) \ + do { \ + if (!(condition)) { \ + JsUtils::ThrowException(env, SFErrorCode::EXCEPTION_PARAMCHECK, message, typeCode); \ + return; \ + } \ + } while (0) + +#define RESULT_CHECK_RETURN(env, condition, errCode, message, typeCode, retVal) \ + do { \ + if (!(condition)) { \ + JsUtils::ThrowException(env, errCode, message, typeCode); \ + return retVal; \ + } \ + } while (0) + +#define RESULT_CHECK_RETURN_VOID(env, condition, errCode, message, typeCode) \ + do { \ + if (!(condition)) { \ + JsUtils::ThrowException(env, errCode, message, typeCode); \ + return; \ + } \ + } while (0) + +/* check condition, return and logging. */ +#define CHECK_RETURN_VOID(condition, message) \ + do { \ + if (!(condition)) { \ + SELECTION_HILOGE("test (" #condition ") failed: " message); \ + return; \ + } \ + } while (0) + +/* check condition, return and logging. */ +#define CHECK_RETURN(condition, message, retVal) \ + do { \ + if (!(condition)) { \ + SELECTION_HILOGE("test (" #condition ") failed: " message); \ + return retVal; \ + } \ + } while (0) + +struct JsPropertyInfo { + napi_valuetype type; + TypeCode typeCode; + std::string propertyName; +}; + +class JsUtils { +public: + static void ThrowException(napi_env env, int32_t err, const std::string &msg, TypeCode type); + + static void ThrowException(napi_env env, int32_t err, const std::string &msg = ""); + + static napi_value ToError(napi_env env, int32_t code, const std::string &msg); + + static int32_t Convert(int32_t code); + + static bool Equals(napi_env env, napi_value value, napi_ref copy, std::thread::id threadId); + + static void *GetNativeSelf(napi_env env, napi_callback_info info); + + static const std::string ToMessage(int32_t code); + + template + static bool ReadOptionalProperty(napi_env env, napi_value object, const JsPropertyInfo &jsPropInfo, T &value) + { + if (!JsUtil::HasProperty(env, object, jsPropInfo.propertyName.c_str())) { + return false; + } + napi_value jsObject = nullptr; + napi_get_named_property(env, object, jsPropInfo.propertyName.c_str(), &jsObject); + PARAM_CHECK_RETURN(env, JsUtil::GetType(env, jsObject) == jsPropInfo.type, jsPropInfo.propertyName, + jsPropInfo.typeCode, false); + PARAM_CHECK_RETURN(env, JsUtils::GetValue(env, jsObject, value) == napi_ok, + "failed to convert " + jsPropInfo.propertyName, TYPE_NONE, false); + return true; + } + + static napi_status GetValue(napi_env env, napi_value in, int32_t &out); + static napi_status GetValue(napi_env env, napi_value in, uint32_t &out); + static napi_status GetValue(napi_env env, napi_value in, bool &out); + static napi_status GetValue(napi_env env, napi_value in, double &out); + static napi_status GetValue(napi_env env, napi_value in, std::string &out); + + + static napi_status GetValue(napi_env env, napi_value in, const std::string &type, napi_value &out); + static napi_status GetValue(napi_env env, napi_value in, std::vector &out); + + + static napi_value GetValue(napi_env env, const std::vector &in); + static napi_status GetValue(napi_env env, const std::string &in, napi_value &out); + +private: + static const std::map ERROR_CODE_MAP; + + static const std::map ERROR_CODE_CONVERT_MESSAGE_MAP; + + static const std::map PARAMETER_TYPE; + + static constexpr int32_t ERROR_CODE_QUERY_FAILED = 1; + + static constexpr uint8_t MAX_ARGMENT_COUNT = 10; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // SELECTAIONFWK_JS_UTILS_H diff --git a/common/selectionfwk_js_utils.cpp b/common/selectionfwk_js_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6154274531705f84d9294653d262576fdb11ea7a --- /dev/null +++ b/common/selectionfwk_js_utils.cpp @@ -0,0 +1,269 @@ +/* + * 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 "selection_js_utils.h" + +namespace OHOS { +namespace SelectionFwk { +constexpr int32_t STR_MAX_LENGTH = 4096; +constexpr size_t STR_TAIL_LENGTH = 1; +constexpr size_t ARGC_MAX = 6; + +const std::map JsUtils::ERROR_CODE_MAP = { + { ErrorCode::ERROR_PARAMETER_CHECK_FAILED, EXCEPTION_PARAMCHECK }, + { ErrorCode::ERROR_SELECTION_SERVICE, EXCEPTION_SELECTION_SERVICE }, + { ErrorCode::ERROR_PANEL_DESTROYED, EXCEPTION_PANEL_DESTROYED }, + { ErrorCode::ERROR_INVALID_OPERATION, EXCEPTION_INVALID_OPERATION } +}; + +const std::map JsUtils::ERROR_CODE_CONVERT_MESSAGE_MAP = { + { EXCEPTION_PARAMCHECK, "The parameters check fails." }, + { EXCEPTION_SELECTION_SERVICE, "Selection service exception." }, + { EXCEPTION_PANEL_DESTROYED, "This selection window has been destroyed." }, + { EXCEPTION_INVALID_OPERATION, "Invalid operation. The selection app is not valid." } +}; + +const std::map JsUtils::PARAMETER_TYPE = { + { TYPE_UNDEFINED, "napi_undefine." }, + { TYPE_NULL, "napi_null." }, + { TYPE_BOOLEAN, "napi_boolean." }, + { TYPE_NUMBER, "napi_number." }, + { TYPE_STRING, "napi_string." }, + { TYPE_SYMBOL, "napi_symbol." }, + { TYPE_OBJECT, "napi_object." }, + { TYPE_FUNCTION, "napi_function." }, + { TYPE_EXTERNAL, "napi_external." }, + { TYPE_BIGINT, "napi_bigint." }, + { TYPE_ARRAY_BUFFER, "ArrayBuffer." }, + { TYPE_ARRAY, "napi_array." }, +}; + +void JsUtils::ThrowException(napi_env env, int32_t err, const std::string &msg, TypeCode type) +{ + std::string errMsg = ToMessage(err); + napi_value error; + napi_value code; + napi_value message; + if (type == TypeCode::TYPE_NONE) { + errMsg = errMsg + " " + msg; + SELECTION_HILOGE("THROW_ERROR message: %{public}s!", errMsg.c_str()); + } else { + auto iter = PARAMETER_TYPE.find(type); + if (iter != PARAMETER_TYPE.end()) { + errMsg = errMsg + "The type of " + msg + " must be " + iter->second; + SELECTION_HILOGE("THROW_ERROR message: %{public}s!", errMsg.c_str()); + } + } + NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &message)); + NAPI_CALL_RETURN_VOID(env, napi_create_error(env, nullptr, message, &error)); + NAPI_CALL_RETURN_VOID(env, napi_create_int32(env, err, &code)); + NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, error, "code", code)); + NAPI_CALL_RETURN_VOID(env, napi_throw(env, error)); +} + +napi_value JsUtils::ToError(napi_env env, int32_t code, const std::string &msg) +{ + SELECTION_HILOGD("ToError start"); + napi_value errorObj; + NAPI_CALL(env, napi_create_object(env, &errorObj)); + napi_value errorCode = nullptr; + NAPI_CALL(env, napi_create_int32(env, Convert(code), &errorCode)); + napi_value errorMessage = nullptr; + std::string errMsg = ToMessage(Convert(code)) + " " + msg; + NAPI_CALL(env, napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &errorMessage)); + NAPI_CALL(env, napi_set_named_property(env, errorObj, "code", errorCode)); + NAPI_CALL(env, napi_set_named_property(env, errorObj, "message", errorMessage)); + SELECTION_HILOGD("ToError end"); + return errorObj; +} + +int32_t JsUtils::Convert(int32_t code) +{ + SELECTION_HILOGD("Convert start."); + auto iter = ERROR_CODE_MAP.find(code); + if (iter != ERROR_CODE_MAP.end()) { + SELECTION_HILOGD("ErrorCode: %{public}d", iter->second); + return iter->second; + } + SELECTION_HILOGD("Convert end."); + return ERROR_CODE_QUERY_FAILED; +} + +const std::string JsUtils::ToMessage(int32_t code) +{ + SELECTION_HILOGD("ToMessage start"); + auto iter = ERROR_CODE_CONVERT_MESSAGE_MAP.find(code); + if (iter != ERROR_CODE_CONVERT_MESSAGE_MAP.end()) { + SELECTION_HILOGD("ErrorMessage: %{public}s", (iter->second).c_str()); + return iter->second; + } + return "error is out of definition."; +} + +bool JsUtils::Equals(napi_env env, napi_value value, napi_ref copy, std::thread::id threadId) +{ + if (copy == nullptr) { + return value == nullptr; + } + + if (threadId != std::this_thread::get_id()) { + SELECTION_HILOGD("napi_value can not be compared"); + return false; + } + + napi_value copyValue = nullptr; + napi_get_reference_value(env, copy, ©Value); + + bool isEquals = false; + napi_strict_equals(env, value, copyValue, &isEquals); + SELECTION_HILOGD("value compare result: %{public}d", isEquals); + return isEquals; +} + +void *JsUtils::GetNativeSelf(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_MAX; + void *native = nullptr; + napi_value self = nullptr; + napi_value argv[ARGC_MAX] = { nullptr }; + napi_status status = napi_invalid_arg; + napi_get_cb_info(env, info, &argc, argv, &self, nullptr); + CHECK_RETURN((self != nullptr && argc <= ARGC_MAX), "napi_get_cb_info failed!", nullptr); + + status = napi_unwrap(env, self, &native); + CHECK_RETURN((status == napi_ok && native != nullptr), "napi_unwrap failed!", nullptr); + return native; +} + +napi_status JsUtils::GetValue(napi_env env, napi_value in, int32_t &out) +{ + SELECTION_HILOGD("napi_value -> int32_t "); + napi_valuetype type = napi_undefined; + napi_status status = napi_typeof(env, in, &type); + CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid type", napi_generic_failure); + return napi_get_value_int32(env, in, &out); +} + +/* napi_value <-> uint32_t */ +napi_status JsUtils::GetValue(napi_env env, napi_value in, uint32_t &out) +{ + napi_valuetype type = napi_undefined; + napi_status status = napi_typeof(env, in, &type); + CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid type", napi_generic_failure); + return napi_get_value_uint32(env, in, &out); +} + +napi_status JsUtils::GetValue(napi_env env, napi_value in, bool &out) +{ + napi_valuetype type = napi_undefined; + napi_status status = napi_typeof(env, in, &type); + CHECK_RETURN((status == napi_ok) && (type == napi_boolean), "invalid type", napi_generic_failure); + return napi_get_value_bool(env, in, &out); +} + +napi_status JsUtils::GetValue(napi_env env, napi_value in, double &out) +{ + napi_valuetype type = napi_undefined; + napi_status status = napi_typeof(env, in, &type); + CHECK_RETURN((status == napi_ok) && (type == napi_number), "invalid double type", napi_generic_failure); + return napi_get_value_double(env, in, &out); +} + +/* napi_value <-> std::string */ +napi_status JsUtils::GetValue(napi_env env, napi_value in, std::string &out) +{ + SELECTION_HILOGD("JsUtils get string value in."); + napi_valuetype type = napi_undefined; + napi_status status = napi_typeof(env, in, &type); + CHECK_RETURN((status == napi_ok) && (type == napi_string), "invalid type", napi_generic_failure); + + size_t maxLen = STR_MAX_LENGTH; + status = napi_get_value_string_utf8(env, in, NULL, 0, &maxLen); + if (maxLen <= 0) { + return status; + } + SELECTION_HILOGD("napi_value -> std::string get length %{public}zu", maxLen); + char *buf = new (std::nothrow) char[maxLen + STR_TAIL_LENGTH]; + if (buf != nullptr) { + size_t len = 0; + status = napi_get_value_string_utf8(env, in, buf, maxLen + STR_TAIL_LENGTH, &len); + if (status == napi_ok) { + buf[len] = 0; + out = std::string(buf); + } + delete[] buf; + } else { + status = napi_generic_failure; + } + return status; +} + +/* napi_value <-> std::unordered_map */ + +napi_status JsUtils::GetValue(napi_env env, napi_value in, const std::string &type, napi_value &out) +{ + napi_valuetype valueType = napi_undefined; + napi_status status = napi_typeof(env, in, &valueType); + if ((status == napi_ok) && (valueType == napi_object)) { + status = napi_get_named_property(env, in, type.c_str(), &out); + return status; + } + return napi_generic_failure; +} + +napi_status JsUtils::GetValue(napi_env env, const std::string &in, napi_value &out) +{ + return napi_create_string_utf8(env, in.c_str(), in.size(), &out); +} + +napi_value JsUtils::GetValue(napi_env env, const std::vector &in) +{ + void *data = nullptr; + napi_value arrayBuffer = nullptr; + size_t length = in.size(); + NAPI_CALL(env, napi_create_arraybuffer(env, length, &data, &arrayBuffer)); + // 0 means the size of data. + CHECK_RETURN(length != 0, "Data size is 0.", arrayBuffer); + if (memcpy_s(data, length, reinterpret_cast(in.data()), length) != 0) { + return nullptr; + } + return arrayBuffer; +} + +napi_status JsUtils::GetValue(napi_env env, napi_value in, std::vector &out) +{ + size_t length = 0; + void *data = nullptr; + auto status = napi_get_arraybuffer_info(env, in, &data, &length); + if (status != napi_ok) { + SELECTION_HILOGE("Get ArrayBuffer info failed!"); + return status; + } + if (data == nullptr && length == 0) { + SELECTION_HILOGE("Empty ArrayBuffer."); + out.clear(); + return napi_ok; + } + if (data == nullptr) { + SELECTION_HILOGE("ArrayBuffer data is nullptr!"); + return napi_generic_failure; + } + SELECTION_HILOGD("ArrayBuffer data size: %{public}zu.", length); + out.assign(reinterpret_cast(data), reinterpret_cast(data) + length); + return napi_ok; +} + +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/common/selectionmethod_trace.cpp b/common/selectionmethod_trace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0aa7f90fe2bf39e3aabf90858aa4ea544565aa5b --- /dev/null +++ b/common/selectionmethod_trace.cpp @@ -0,0 +1,59 @@ +/* + * 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 "selectionmethod_trace.h" + +#include "hitrace_meter.h" + +namespace OHOS { +namespace SelectionFwk { +constexpr uint64_t HITRACE_TAG_MISC = (1ULL << 41); // Notification module tag. +void InitHiTrace() +{ + UpdateTraceLabel(); +} + +void ValueTrace(const std::string &name, int64_t count) +{ + CountTrace(HITRACE_TAG_MISC, name, count); +} + +void StartAsync(const std::string &value, int32_t taskId) +{ + StartAsyncTrace(HITRACE_TAG_MISC, value, taskId); +} + +void FinishAsync(const std::string &value, int32_t taskId) +{ + FinishAsyncTrace(HITRACE_TAG_MISC, value, taskId); +} + +SelectionMethodSyncTrace::SelectionMethodSyncTrace(const std::string &value) +{ + StartTrace(HITRACE_TAG_MISC, value); +} + +SelectionMethodSyncTrace::SelectionMethodSyncTrace(const std::string &value, const std::string &id) +{ + auto info = value + "_" + id; + StartTrace(HITRACE_TAG_MISC, info); +} + +SelectionMethodSyncTrace::~SelectionMethodSyncTrace() +{ + FinishTrace(HITRACE_TAG_MISC); +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/common/selectionmethod_trace.h b/common/selectionmethod_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..8ed9bd3afbb52c85292c1310821d9b24a2bed5fd --- /dev/null +++ b/common/selectionmethod_trace.h @@ -0,0 +1,47 @@ +/* + * 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 SELECTIONMETHOD_TRACE_H +#define SELECTIONMETHOD_TRACE_H + +#include + +namespace OHOS { +namespace SelectionFwk { +void InitHiTrace(); +void ValueTrace(const std::string &name, int64_t count); + +void StartAsync(const std::string &value, int32_t taskId); +void FinishAsync(const std::string &value, int32_t taskId); + +class SelectionMethodSyncTrace { +public: + explicit SelectionMethodSyncTrace(const std::string &value); + SelectionMethodSyncTrace(const std::string &value, const std::string &id); + virtual ~SelectionMethodSyncTrace(); +}; + +enum class TraceTaskId : int32_t { + ONSTART_EXTENSION, + ONSTART_MIDDLE_EXTENSION, + ONCREATE_EXTENSION, + ONCONNECT_EXTENSION, + ONCONNECT_MIDDLE_EXTENSION, + ON_KEY_EVENT, + ON_FULL_KEY_EVENT, +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // SELECTIONMETHOD_TRACE_H \ No newline at end of file diff --git a/common/util.cpp b/common/util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1c25c217760a40407c876f0bd8957525b488848 --- /dev/null +++ b/common/util.cpp @@ -0,0 +1,122 @@ +/* + * 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 "util.h" +#include "selection_log.h" +#include "string_ex.h" + +namespace OHOS { +namespace SelectionFwk { +constexpr int64_t JS_NUMBER_MAX_VALUE = (1LL << 53) - 1; +napi_valuetype JsUtil::GetType(napi_env env, napi_value in) +{ + napi_valuetype valueType = napi_undefined; + napi_status status = napi_typeof(env, in, &valueType); + if (status != napi_ok) { + SELECTION_HILOGE("get value of valueType failed."); + } + return valueType; +} +bool JsUtil::HasProperty(napi_env env, napi_value object, const std::string &property) +{ + bool hasProperty = false; + napi_status status = napi_has_named_property(env, object, property.c_str(), &hasProperty); + if (status == napi_ok && hasProperty) { + return true; + } + return false; +} +bool JsUtil::GetValue(napi_env env, napi_value in, std::string &out) +{ + size_t size = 0; + auto status = napi_get_value_string_utf8(env, in, nullptr, 0, &size); + if (status != napi_ok) { + return false; + } + out.resize(size + 1, 0); + status = napi_get_value_string_utf8(env, in, const_cast(out.data()), size + 1, &size); + out.resize(size); + return status == napi_ok; +} + +bool JsUtil::GetValue(napi_env env, napi_value in, std::u16string &out) +{ + std::string tempOut; + bool ret = GetValue(env, in, tempOut); + if (ret) { + out = Str8ToStr16(tempOut); + } + return ret; +} +bool JsUtil::GetValue(napi_env env, napi_value in, int32_t &out) +{ + return napi_get_value_int32(env, in, &out) == napi_ok; +} +bool JsUtil::GetValue(napi_env env, napi_value in, uint32_t &out) +{ + return napi_get_value_uint32(env, in, &out) == napi_ok; +} +bool JsUtil::GetValue(napi_env env, napi_value in, int64_t &out) +{ + return napi_get_value_int64(env, in, &out) == napi_ok; +} +bool JsUtil::GetValue(napi_env env, napi_value in, bool &out) +{ + return napi_get_value_bool(env, in, &out) == napi_ok; +} +bool JsUtil::GetValue(napi_env env, napi_value in, double &out) +{ + return napi_get_value_double(env, in, &out) == napi_ok; +} +napi_value JsUtil::GetValue(napi_env env, napi_value in) +{ + return in; +} +napi_value JsUtil::GetValue(napi_env env, const std::string &in) +{ + napi_value out = nullptr; + napi_create_string_utf8(env, in.c_str(), in.length(), &out); + return out; +} +napi_value JsUtil::GetValue(napi_env env, int32_t in) +{ + napi_value out = nullptr; + napi_create_int32(env, in, &out); + return out; +} +napi_value JsUtil::GetValue(napi_env env, uint32_t in) +{ + napi_value out = nullptr; + napi_create_uint32(env, in, &out); + return out; +} +napi_value JsUtil::GetValue(napi_env env, int64_t in) +{ + if (in < -JS_NUMBER_MAX_VALUE || in > JS_NUMBER_MAX_VALUE) { + // cannot exceed the range of js + return nullptr; + } + napi_value out = nullptr; + napi_create_int64(env, in, &out); + return out; +} +napi_value JsUtil::GetValue(napi_env env, bool in) +{ + napi_value out = nullptr; + napi_get_boolean(env, in, &out); + return out; +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/common/util.h b/common/util.h new file mode 100644 index 0000000000000000000000000000000000000000..5c43794c323225640e8ecb46ea0285b5465c5561 --- /dev/null +++ b/common/util.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 OHOS_UTIL_H +#define OHOS_UTIL_H +#include +#include + +#include "napi/native_api.h" +#include "napi/native_node_api.h" +namespace OHOS { +namespace SelectionFwk { +class JsUtil { +public: + static napi_valuetype GetType(napi_env env, napi_value in); + static bool HasProperty(napi_env env, napi_value object, const std::string &property); + // js to native + static bool GetValue(napi_env env, napi_value in, std::string &out); + static bool GetValue(napi_env env, napi_value in, std::u16string &out); + static bool GetValue(napi_env env, napi_value in, int32_t &out); + static bool GetValue(napi_env env, napi_value in, uint32_t &out); + static bool GetValue(napi_env env, napi_value in, int64_t &out); + static bool GetValue(napi_env env, napi_value in, bool &out); + static bool GetValue(napi_env env, napi_value in, double &out); + template + static bool GetValue(napi_env env, napi_value in, std::vector &items) + { + uint32_t len = 0; + napi_get_array_length(env, in, &len); + items.resize(len); + for (uint32_t i = 0; i < len; i++) { + napi_value item = nullptr; + auto status = napi_get_element(env, in, i, &item); + T buff{}; + if (status != napi_ok || !GetValue(env, item, buff)) { + return false; + } + items[i] = std::move(buff); + } + return true; + } + + // native to js + static napi_value GetValue(napi_env env, napi_value in); + static napi_value GetValue(napi_env env, const std::string &in); + static napi_value GetValue(napi_env env, int32_t in); + static napi_value GetValue(napi_env env, uint32_t in); + static napi_value GetValue(napi_env env, int64_t in); + static napi_value GetValue(napi_env env, bool in); + template + static napi_value GetValue(napi_env env, const std::vector &items) + { + napi_value array = nullptr; + auto status = napi_create_array(env, &array); + if (status != napi_ok) { + return nullptr; + } + uint32_t index = 0; + for (const T &item : items) { + auto itemValue = GetValue(env, item); + if (itemValue == nullptr) { + return nullptr; + } + status = napi_set_element(env, array, index++, itemValue); + if (status != napi_ok) { + return nullptr; + } + } + return array; + } + class Object { + public: + template + static bool WriteProperty(napi_env env, napi_value object, const std::string &property, const T &value) + { + return napi_set_named_property(env, object, property.c_str(), GetValue(env, value)) == napi_ok; + } + template + static bool ReadProperty(napi_env env, napi_value object, const std::string &property, T &value) + { + napi_value propValue = nullptr; + napi_get_named_property(env, object, property.c_str(), &propValue); + return GetValue(env, propValue, value); + } + }; + class Const { + public: + static napi_value Null(napi_env env) + { + napi_value value = nullptr; + napi_get_null(env, &value); + return value; + } + static napi_value Undefined(napi_env env) + { + napi_value value = nullptr; + napi_get_undefined(env, &value); + return value; + } + }; + class ScopeGuard { + public: + explicit ScopeGuard(napi_env env) : env_(env), scope_(nullptr) + { + napi_open_handle_scope(env_, &scope_); + } + ~ScopeGuard() + { + if (scope_ != nullptr) { + napi_close_handle_scope(env_, scope_); + } + } + + private: + napi_env env_; + napi_handle_scope scope_; + }; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // OHOS_UTIL_H diff --git a/etc/init/BUILD.gn b/etc/init/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..6da8595b1aef0dc11a465211d8759a07ba239acd --- /dev/null +++ b/etc/init/BUILD.gn @@ -0,0 +1,20 @@ +# 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/ohos.gni") + +ohos_prebuilt_etc("selection_service_cfg") { + source = "selection_service.cfg" + relative_install_dir = "init" + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} \ No newline at end of file diff --git a/etc/init/selection_service.cfg b/etc/init/selection_service.cfg new file mode 100644 index 0000000000000000000000000000000000000000..89d60260de0688822f3ef1a90548ae0793f4ea38 --- /dev/null +++ b/etc/init/selection_service.cfg @@ -0,0 +1,37 @@ +{ + "services" : [{ + "name" : "selection_service", + "path" : ["/system/bin/sa_main", "/system/profile/selection_service.json"], + "ondemand": true, + "importance" : -20, + "once" : 0, + "uid" : "sysselection", + "gid" : ["sysselection", "shell"], + "apl": "system_basic", + "permission" : [ + "ohos.permission.INPUT_MONITORING", + "ohos.permission.INJECT_INPUT_EVENT", + "ohos.permission.GET_RUNNING_INFO", + "ohos.permission.GET_BUNDLE_INFO_PRIVILEGED", + "ohos.permission.READ_PASTEBOARD", + "ohos.permission.REPORT_RESOURCE_SCHEDULE_EVENT", + "ohos.permission.CONNECT_SELECTION_EXTENSION", + "ohos.permission.SYSTEM_FLOAT_WINDOW" + ], + "jobs" : { + "on-start" : "services:selection_service" + }, + "permission_acls" : ["ohos.permission.INPUT_MONITORING"], + "caps" : [], + "secon" : "u:r:selection_service:s0" + } + ], + "jobs" : [{ + "name" : "services:selection_service", + "cmds" : [ + "mkdir /data/service/el1/public/selection_service 0770 sysselection sysselection", + "chown sysselection sysselection /data/service/el1/public/selection_service", + "chmod 0770 /data/service/el1/public/selection_service" + ] + }] +} diff --git a/etc/para/BUILD.gn b/etc/para/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2fb6af2c7a70db6fa8ec2ef0f5f636e7a665df37 --- /dev/null +++ b/etc/para/BUILD.gn @@ -0,0 +1,28 @@ +# 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/ohos.gni") + +ohos_prebuilt_etc("selection_para") { + source = "./selection.para" + module_install_dir = "etc/param" + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} + +ohos_prebuilt_etc("selection_para_dac") { + source = "./selection.para.dac" + module_install_dir = "etc/param" + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} diff --git a/etc/para/selection.para b/etc/para/selection.para new file mode 100644 index 0000000000000000000000000000000000000000..aac60704cf5f1ff1496eb049b71367f3cb10f216 --- /dev/null +++ b/etc/para/selection.para @@ -0,0 +1,17 @@ +# 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. + +sys.selection.switch = on +sys.selection.trigger = immediate +sys.selection.app = com.huawei.hmos.vassistant/MiniMenuServiceExtAbility +sys.selection.uid = -1 diff --git a/etc/para/selection.para.dac b/etc/para/selection.para.dac new file mode 100644 index 0000000000000000000000000000000000000000..91f5f12805df3d35322b15437133cbcb01c1d2d1 --- /dev/null +++ b/etc/para/selection.para.dac @@ -0,0 +1,14 @@ +# 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. + +sys.selection. = sysselection:sysselection:0775 \ No newline at end of file diff --git a/frameworks/common/concurrent_map.h b/frameworks/common/concurrent_map.h new file mode 100644 index 0000000000000000000000000000000000000000..415db14ff33e0f74bc2c0dc21913282d681810b8 --- /dev/null +++ b/frameworks/common/concurrent_map.h @@ -0,0 +1,302 @@ +/* + * 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 OHOS_SELECTIONFWK_FRAMEWORKS_COMMON_CONCURRENT_MAP_H +#define OHOS_SELECTIONFWK_FRAMEWORKS_COMMON_CONCURRENT_MAP_H +#include +#include +#include +namespace OHOS { +template +class ConcurrentMap { + template + static _First First(); + +public: + using map_type = typename std::map<_Key, _Tp>; + using filter_type = typename std::function; + using key_type = typename std::map<_Key, _Tp>::key_type; + using mapped_type = typename std::map<_Key, _Tp>::mapped_type; + using value_type = typename std::map<_Key, _Tp>::value_type; + using size_type = typename std::map<_Key, _Tp>::size_type; + using reference = typename std::map<_Key, _Tp>::reference; + using const_reference = typename std::map<_Key, _Tp>::const_reference; + + ConcurrentMap() = default; + ~ConcurrentMap() + { + Clear(); + } + + ConcurrentMap(const ConcurrentMap &other) + { + operator=(std::move(other)); + } + + ConcurrentMap &operator=(const ConcurrentMap &other) noexcept + { + if (this == &other) { + return *this; + } + auto tmp = other.Clone(); + std::lock_guard lock(mutex_); + entries_ = std::move(tmp); + return *this; + } + + ConcurrentMap(ConcurrentMap &&other) noexcept + { + operator=(std::move(other)); + } + + ConcurrentMap &operator=(ConcurrentMap &&other) noexcept + { + if (this == &other) { + return *this; + } + auto tmp = other.Steal(); + std::lock_guard lock(mutex_); + entries_ = std::move(tmp); + return *this; + } + + bool Emplace() noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.emplace(); + return it.second; + } + + template + typename std::enable_if()), filter_type>, bool>::type Emplace( + _Args &&...args) noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.emplace(std::forward<_Args>(args)...); + return it.second; + } + + template + typename std::enable_if, bool>::type Emplace(const _Filter &filter, + _Args &&...args) noexcept + { + std::lock_guard lock(mutex_); + if (!filter(entries_)) { + return false; + } + auto it = entries_.emplace(std::forward<_Args>(args)...); + return it.second; + } + + std::pair Find(const key_type &key) const noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it == entries_.end()) { + return std::pair{ false, mapped_type() }; + } + + return std::pair{ true, it->second }; + } + + bool Contains(const key_type &key) const noexcept + { + std::lock_guard lock(mutex_); + return (entries_.find(key) != entries_.end()); + } + + template + bool InsertOrAssign(const key_type &key, _Obj &&obj) noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.insert_or_assign(key, std::forward<_Obj>(obj)); + return it.second; + } + + bool Insert(const key_type &key, const mapped_type &value) noexcept + { + std::lock_guard lock(mutex_); + auto it = entries_.insert(value_type{ key, value }); + return it.second; + } + + size_type Erase(const key_type &key) noexcept + { + std::lock_guard lock(mutex_); + return entries_.erase(key); + } + + void Clear() noexcept + { + std::lock_guard lock(mutex_); + return entries_.clear(); + } + + bool Empty() const noexcept + { + std::lock_guard lock(mutex_); + return entries_.empty(); + } + + size_type Size() const noexcept + { + std::lock_guard lock(mutex_); + return entries_.size(); + } + + // The action`s return true means meeting the erase condition + // The action`s return false means not meeting the erase condition + size_type EraseIf(const std::function &action) noexcept + { + if (action == nullptr) { + return 0; + } + std::lock_guard lock(mutex_); +#if __cplusplus > 201703L + auto count = std::erase_if(entries_, + [&action](value_type &value) -> bool { return action(value.first, value.second); }); +#else + auto count = entries_.size(); + for (auto it = entries_.begin(); it != entries_.end();) { + if (action((*it).first, (*it).second)) { + it = entries_.erase(it); + } else { + ++it; + } + } + count -= entries_.size(); +#endif + return count; + } + + void ForEach(const std::function &action) + { + if (action == nullptr) { + return; + } + std::lock_guard lock(mutex_); + for (auto &[key, value] : entries_) { + if (action(key, value)) { + break; + } + } + } + + void ForEachCopies(const std::function &action) + { + if (action == nullptr) { + return; + } + auto entries = Clone(); + for (auto &[key, value] : entries) { + if (action(key, value)) { + break; + } + } + } + + // The action's return value means that the element is keep in map or not; true means keeping, false means removing. + bool Compute(const key_type &key, const std::function &action) + { + if (action == nullptr) { + return false; + } + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it == entries_.end()) { + auto result = entries_.emplace(key, mapped_type()); + it = result.second ? result.first : entries_.end(); + } + if (it == entries_.end()) { + return false; + } + if (!action(it->first, it->second)) { + entries_.erase(key); + } + return true; + } + + // The action's return value means that the element is keep in map or not; true means keeping, false means removing. + bool ComputeIfPresent(const key_type &key, const std::function &action) + { + if (action == nullptr) { + return false; + } + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it == entries_.end()) { + return false; + } + if (!action(key, it->second)) { + entries_.erase(key); + } + return true; + } + + bool ComputeIfAbsent(const key_type &key, const std::function &action) + { + if (action == nullptr) { + return false; + } + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it != entries_.end()) { + return false; + } + entries_.emplace(key, action(key)); + return true; + } + + bool ComputeIfAbsent(const key_type &key, const std::function &action) + { + if (action == nullptr) { + return false; + } + std::lock_guard lock(mutex_); + auto it = entries_.find(key); + if (it != entries_.end()) { + return false; + } + auto result = entries_.emplace(key, mapped_type()); + it = result.second ? result.first : entries_.end(); + if (it == entries_.end()) { + return false; + } + if (!action(it->first, it->second)) { + entries_.erase(key); + return false; + } + return true; + } + +private: + std::map<_Key, _Tp> Steal() noexcept + { + std::lock_guard lock(mutex_); + return std::move(entries_); + } + + std::map<_Key, _Tp> Clone() const noexcept + { + std::lock_guard lock(mutex_); + return entries_; + } + +private: + mutable std::recursive_mutex mutex_; + std::map<_Key, _Tp> entries_; +}; +} // namespace OHOS +#endif // OHOS_SELECTIONFWK_FRAMEWORKS_COMMON_CONCURRENT_MAP_H diff --git a/frameworks/js/napi/selection_ability/BUILD.gn b/frameworks/js/napi/selection_ability/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..e0a6461b79ba66b040c834024a934fdedccb99c5 --- /dev/null +++ b/frameworks/js/napi/selection_ability/BUILD.gn @@ -0,0 +1,82 @@ +# 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/ohos.gni") +import("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +ohos_shared_library("selectionmanager_napi") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + integer_overflow = true + ubsan = true + } + sources = [ + "js_selection_engine_setting.cpp", + "panel_listener_impl.cpp", + "selection_engine_module.cpp", + "js_panel.cpp", + "../selection_client/async_call.cpp", + "../selection_client/js_selection_utils.cpp", + ] + + include_dirs = [ + ".", + "../../../../utils/include", + "${target_gen_dir}", + "../../../native/selection_ability/include", + "../selection_client", + "../../../common", + "../../../../hiappevent_agent", + ] + + deps = [ + "${selection_fwk_root_path}/common:selection_common", + "${selection_fwk_root_path}/hiappevent_agent:selection_hiappevent_agent", + "${selection_fwk_root_path}/interfaces/idl:selection_service_proxy", + "${selection_fwk_root_path}/interfaces/idl:selection_listener_proxy", + "${selection_fwk_root_path}/frameworks/native/selection_ability:selection_ability", + ] + + cflags_cc = [ "-std=c++17" ] + + external_deps = [ + "ability_base:configuration", + "ability_runtime:ability_context_native", + "ability_runtime:abilitykit_native", + "ability_runtime:extensionkit_native", + "ability_runtime:napi_base_context", + "c_utils:utils", + "eventhandler:libeventhandler", + "hilog:libhilog", + "ipc:ipc_core", + "ipc:ipc_single", + "napi:ace_napi", + "window_manager:libwm_lite", + "resource_management:global_resmgr", + "ability_runtime:app_context", + "ffrt:libffrt", + "graphic_2d:librender_service_base", + "graphic_2d:librender_service_client", + "graphic_2d:window_animation", + "samgr:samgr_proxy", + ] + + relative_install_dir = "module/selectioninput" + subsystem_name = "systemabilitymgr" + part_name = "selectionfwk" +} diff --git a/frameworks/js/napi/selection_ability/js_panel.cpp b/frameworks/js/napi/selection_ability/js_panel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..02a6c286f462c226cd5e69282e4b585d4726dc44 --- /dev/null +++ b/frameworks/js/napi/selection_ability/js_panel.cpp @@ -0,0 +1,398 @@ +/* + * 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 "js_panel.h" + +#include "napi/native_common.h" +#include "napi/native_node_api.h" +#include "selection_log.h" +#include "panel_listener_impl.h" +#include "selection_js_utils.h" +#include "selectionmethod_trace.h" +#include "selection_ability.h" +#include "event_checker.h" +#include "selection_api_event_reporter.h" + +namespace OHOS { +namespace SelectionFwk { +using namespace std::chrono; +constexpr int32_t MAX_WAIT_TIME = 10; +const std::string JsPanel::CLASS_NAME = "Panel"; +thread_local napi_ref JsPanel::panelConstructorRef_ = nullptr; +std::mutex JsPanel::panelConstructorMutex_; +FFRTBlockQueue JsPanel::jsQueue_{ MAX_WAIT_TIME }; +constexpr size_t ARGC_MAX = 6; + +napi_value JsPanel::Init(napi_env env) +{ + SELECTION_HILOGI("JsPanel start."); + napi_value constructor = nullptr; + std::lock_guard lock(panelConstructorMutex_); + if (panelConstructorRef_ != nullptr) { + napi_status status = napi_get_reference_value(env, panelConstructorRef_, &constructor); + CHECK_RETURN(status == napi_ok, "failed to get jsPanel constructor.", nullptr); + return constructor; + } + const napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("setUiContent", SetUiContent), + DECLARE_NAPI_FUNCTION("show", Show), + DECLARE_NAPI_FUNCTION("hide", Hide), + DECLARE_NAPI_FUNCTION("startMoving", StartMoving), + DECLARE_NAPI_FUNCTION("moveTo", MoveTo), + DECLARE_NAPI_FUNCTION("on", Subscribe), + DECLARE_NAPI_FUNCTION("off", UnSubscribe) + }; + NAPI_CALL(env, napi_define_class(env, CLASS_NAME.c_str(), CLASS_NAME.size(), JsNew, nullptr, + sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor)); + CHECK_RETURN(constructor != nullptr, "failed to define class!", nullptr); + NAPI_CALL(env, napi_create_reference(env, constructor, 1, &panelConstructorRef_)); + return constructor; +} + +napi_value JsPanel::JsNew(napi_env env, napi_callback_info info) +{ + SELECTION_HILOGD("create panel instance start."); + std::shared_ptr panelImpl = PanelListenerImpl::GetInstance(); + if (panelImpl != nullptr) { + SELECTION_HILOGD("set eventHandler."); + panelImpl->SetEventHandler(AppExecFwk::EventHandler::Current()); + } + JsPanel *panel = new (std::nothrow) JsPanel(); + CHECK_RETURN(panel != nullptr, "no memory for JsPanel!", nullptr); + auto finalize = [](napi_env env, void *data, void *hint) { + SELECTION_HILOGD("jsPanel finalize."); + auto *jsPanel = reinterpret_cast(data); + CHECK_RETURN_VOID(jsPanel != nullptr, "finalize nullptr!"); + jsPanel->GetNative().reset(); + if (jsPanel != nullptr) { + delete jsPanel; + } + }; + napi_value thisVar = nullptr; + napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr); + if (status != napi_ok) { + SELECTION_HILOGE("failed to get cb info: %{public}d!", status); + delete panel; + return nullptr; + } + status = napi_wrap(env, thisVar, panel, finalize, nullptr, nullptr); + if (status != napi_ok) { + SELECTION_HILOGE("failed to wrap: %{public}d!", status); + delete panel; + return nullptr; + } + return thisVar; +} + +JsPanel::~JsPanel() +{ + selectionPanel_ = nullptr; +} + +void JsPanel::SetNative(const std::shared_ptr &panel) +{ + selectionPanel_ = panel; +} + +std::shared_ptr JsPanel::GetNative() +{ + return selectionPanel_; +} + +napi_value JsPanel::SetUiContent(napi_env env, napi_callback_info info) +{ + SELECTION_HILOGI("JsPanel start."); + auto ctxt = std::make_shared(env, info); + auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + napi_status status = napi_generic_failure; + PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, status); + // 0 means the first param path + PARAM_CHECK_RETURN(env, JsUtils::GetValue(env, argv[0], ctxt->path) == napi_ok, + "js param path covert failed, must be string!", TYPE_NONE, status); + ctxt->info = { std::chrono::system_clock::now(), JsEvent::SET_UI_CONTENT }; + jsQueue_.Push(ctxt->info); + return napi_ok; + }; + + auto exec = [ctxt](AsyncCall::Context *ctx) { ctxt->SetState(napi_ok); }; + auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { + jsQueue_.Wait(ctxt->info); + if (ctxt->selectionPanel == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr!"); + jsQueue_.Pop(); + ctxt->SetErrorCode(SFErrorCode::EXCEPTION_PANEL_DESTROYED); + return napi_generic_failure; + } + auto code = ctxt->selectionPanel->SetUiContent(ctxt->path, env); + jsQueue_.Pop(); + if (code != ErrorCode::NO_ERROR) { + ctxt->SetErrorCode(code); + return napi_generic_failure; + } + return napi_ok; + }; + ctxt->SetAction(std::move(input), std::move(output)); + // 2 means JsAPI:setUiContent has 2 params at most. + AsyncCall asyncCall(env, info, ctxt, 2); + return asyncCall.Call(env, exec, "setUiContent"); +} + +napi_value JsPanel::Show(napi_env env, napi_callback_info info) +{ + SelectionMethodSyncTrace tracer("JsPanel_Show"); + auto ctxt = std::make_shared(env, info); + auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + ctxt->info = { std::chrono::system_clock::now(), JsEvent::SHOW }; + jsQueue_.Push(ctxt->info); + return napi_ok; + }; + auto exec = [ctxt](AsyncCall::Context *ctx) { + jsQueue_.Wait(ctxt->info); + if (ctxt->selectionPanel == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr!"); + jsQueue_.Pop(); + ctxt->SetErrorCode(SFErrorCode::EXCEPTION_PANEL_DESTROYED); + return; + } + auto code = SelectionAbility::GetInstance()->ShowPanel(ctxt->selectionPanel); + jsQueue_.Pop(); + if (code == ErrorCode::NO_ERROR) { + ctxt->SetState(napi_ok); + return; + } + ctxt->SetErrorCode(code); + }; + ctxt->SetAction(std::move(input)); + // 1 means JsAPI:show has 1 param at most. + AsyncCall asyncCall(env, info, ctxt, 1); + return asyncCall.Call(env, exec, "show"); +} + +napi_value JsPanel::Hide(napi_env env, napi_callback_info info) +{ + SelectionMethodSyncTrace tracer("JsPanel_Hide"); + auto ctxt = std::make_shared(env, info); + + auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + ctxt->info = { std::chrono::system_clock::now(), JsEvent::HIDE }; + jsQueue_.Push(ctxt->info); + return napi_ok; + }; + auto exec = [ctxt](AsyncCall::Context *ctx) { + jsQueue_.Wait(ctxt->info); + if (ctxt->selectionPanel == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr!"); + jsQueue_.Pop(); + ctxt->SetErrorCode(SFErrorCode::EXCEPTION_PANEL_DESTROYED); + return; + } + auto code = SelectionAbility::GetInstance()->HidePanel(ctxt->selectionPanel); + jsQueue_.Pop(); + if (code == ErrorCode::NO_ERROR) { + ctxt->SetState(napi_ok); + return; + } + ctxt->SetErrorCode(code); + }; + ctxt->SetAction(std::move(input)); + // 1 means JsAPI:hide has 1 param at most. + AsyncCall asyncCall(env, info, ctxt, 1); + return asyncCall.Call(env, exec, "panel.hide"); +} + +napi_value JsPanel::StartMoving(napi_env env, napi_callback_info info) +{ + SELECTION_HILOGD("StartMoving start!"); + auto ctxt = std::make_shared(env, info); + auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + ctxt->info = { std::chrono::system_clock::now(), JsEvent::START_MOVING }; + jsQueue_.Push(ctxt->info); + return napi_ok; + }; + + auto exec = [ctxt](AsyncCall::Context *ctx) { + jsQueue_.Wait(ctxt->info); + if (ctxt->selectionPanel == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr!"); + jsQueue_.Pop(); + ctxt->SetErrorCode(SFErrorCode::EXCEPTION_PANEL_DESTROYED); + return; + } + auto code = ctxt->selectionPanel->StartMoving(); + jsQueue_.Pop(); + if (code != ErrorCode::NO_ERROR) { + ctxt->SetErrorCode(code); + return; + } + ctxt->SetState(napi_ok); + }; + ctxt->SetAction(std::move(input)); + + AsyncCall asyncCall(env, info, ctxt, 1); + return asyncCall.Call(env, exec, "startMoving"); +} + +napi_value JsPanel::MoveTo(napi_env env, napi_callback_info info) +{ + SELECTION_HILOGD("moveto start!"); + auto ctxt = std::make_shared(env, info); + auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + napi_status status = napi_generic_failure; + PARAM_CHECK_RETURN(env, argc > 1, "at least two parameters is required ", TYPE_NONE, status); + // 0 means the first param x + PARAM_CHECK_RETURN(env, JsUtils::GetValue(env, argv[0], ctxt->x) == napi_ok, "x type must be number", + TYPE_NONE, status); + // 1 means the second param y + PARAM_CHECK_RETURN(env, JsUtils::GetValue(env, argv[1], ctxt->y) == napi_ok, "y type must be number", + TYPE_NONE, status); + PARAM_CHECK_RETURN(env, ctxt->x >= 0 && ctxt->y >= 0, "x/y must be positive", + TYPE_NONE, status); + ctxt->info = { std::chrono::system_clock::now(), JsEvent::MOVE_TO }; + jsQueue_.Push(ctxt->info); + return napi_ok; + }; + + auto exec = [ctxt](AsyncCall::Context *ctx) { + int64_t start = duration_cast(system_clock::now().time_since_epoch()).count(); + jsQueue_.Wait(ctxt->info); + PrintEditorQueueInfoIfTimeout(start, ctxt->info); + if (ctxt->selectionPanel == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr!"); + jsQueue_.Pop(); + ctxt->SetErrorCode(SFErrorCode::EXCEPTION_PANEL_DESTROYED); + return; + } + auto code = ctxt->selectionPanel->MoveTo(ctxt->x, ctxt->y); + jsQueue_.Pop(); + if(code != ErrorCode::NO_ERROR) { + ctxt->SetErrorCode(code); + return; + } + ctxt->SetState(napi_ok); + }; + ctxt->SetAction(std::move(input)); + // 3 means JsAPI:moveTo has 3 params at most. + AsyncCall asyncCall(env, info, ctxt, 3); + return asyncCall.Call(env, exec, "moveTo"); +} + +void JsPanel::PrintEditorQueueInfoIfTimeout(int64_t start, const JsEventInfo ¤tInfo) +{ + int64_t end = duration_cast(system_clock::now().time_since_epoch()).count(); + if (end - start >= MAX_WAIT_TIME) { + JsEventInfo frontInfo; + auto ret = jsQueue_.GetFront(frontInfo); + int64_t frontTime = duration_cast(frontInfo.timestamp.time_since_epoch()).count(); + int64_t currentTime = duration_cast(currentInfo.timestamp.time_since_epoch()).count(); + SELECTION_HILOGI("ret:%{public}d,front[%{public}" PRId64 ",%{public}d],current[%{public}" PRId64 + ",%{public}d]", ret, frontTime, static_cast(frontInfo.event), currentTime, + static_cast(currentInfo.event)); + } +} + +napi_value JsPanel::Subscribe(napi_env env, napi_callback_info info) +{ + SELECTION_HILOGD("JsPanel start."); + size_t argc = ARGC_MAX; + napi_value argv[ARGC_MAX] = {nullptr}; + napi_value thisVar = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr)); + std::string type; + // 2 means least param num. + if (argc < 2 || !JsUtil::GetValue(env, argv[0], type) || + !EventChecker::IsValidEventType(EventSubscribeModule::PANEL, type) || + JsUtil::GetType(env, argv[1]) != napi_function) { + SELECTION_HILOGE("subscribe failed, type: %{public}s!", type.c_str()); + JsUtils::ThrowException(env, SFErrorCode::EXCEPTION_PARAMCHECK, "", TYPE_NONE); + return nullptr; + } + SELECTION_HILOGD("subscribe type: %{public}s.", type.c_str()); + std::shared_ptr observer = PanelListenerImpl::GetInstance(); + auto selectionPanel = UnwrapPanel(env, thisVar); + if (selectionPanel == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr!"); + return nullptr; + } + // 1 means the second param callback. + std::shared_ptr cbObject = std::make_shared( + env, argv[1], std::this_thread::get_id(), AppExecFwk::EventHandler::Current()); + observer->Subscribe(selectionPanel->windowId_, type, cbObject); + bool ret = selectionPanel->SetPanelStatusListener(observer, type); + if (!ret) { + SELECTION_HILOGE("failed to subscribe %{public}s!", type.c_str()); + observer->RemoveInfo(type, selectionPanel->windowId_); + } + napi_value result = nullptr; + napi_get_undefined(env, &result); + return result; +} + +napi_value JsPanel::UnSubscribe(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_MAX; + napi_value argv[ARGC_MAX] = { nullptr }; + napi_value thisVar = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr)); + std::string type; + // 1 means least param num. + PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, nullptr); + PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], type), "type must be string!", TYPE_NONE, nullptr); + PARAM_CHECK_RETURN(env, EventChecker::IsValidEventType(EventSubscribeModule::PANEL, type), + "type should be show/hide/sizeChange!", TYPE_NONE, nullptr); + // if the second param is not napi_function/napi_null/napi_undefined, return + auto paramType = JsUtil::GetType(env, argv[1]); + PARAM_CHECK_RETURN(env, (paramType == napi_function || paramType == napi_null || paramType == napi_undefined), + "callback should be function or null or undefined!", TYPE_NONE, nullptr); + // if the second param is napi_function, delete it, else delete all + argv[1] = paramType == napi_function ? argv[1] : nullptr; + + SELECTION_HILOGD("unsubscribe type: %{public}s.", type.c_str()); + std::shared_ptr observer = PanelListenerImpl::GetInstance(); + auto selectionPanel = UnwrapPanel(env, thisVar); + if (selectionPanel == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr!"); + return nullptr; + } + + if (paramType == napi_function) { + std::shared_ptr cbObject = std::make_shared( + env, argv[1], std::this_thread::get_id(), AppExecFwk::EventHandler::Current()); + observer->RemoveInfo(type, selectionPanel->windowId_, cbObject); + } else { + observer->RemoveInfo(type, selectionPanel->windowId_); + selectionPanel->ClearPanelListener(type); + } + + napi_value result = nullptr; + napi_get_null(env, &result); + return result; +} + +std::shared_ptr JsPanel::UnwrapPanel(napi_env env, napi_value thisVar) +{ + void *native = nullptr; + napi_status status = napi_unwrap(env, thisVar, &native); + CHECK_RETURN((status == napi_ok && native != nullptr), "failed to unwrap!", nullptr); + auto jsPanel = reinterpret_cast(native); + if (jsPanel == nullptr) { + return nullptr; + } + auto selectionPanel = jsPanel->GetNative(); + CHECK_RETURN(selectionPanel != nullptr, "SelectionPanel is nullptr", nullptr); + return selectionPanel; +} + +} // namespace SelectionFwk +} // namespace OHOS diff --git a/frameworks/js/napi/selection_ability/js_panel.h b/frameworks/js/napi/selection_ability/js_panel.h new file mode 100644 index 0000000000000000000000000000000000000000..8558d7c318edd627a7dc8c986daa25bb569585db --- /dev/null +++ b/frameworks/js/napi/selection_ability/js_panel.h @@ -0,0 +1,123 @@ +/* + * 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 SELECTION_JSPANEL_H +#define SELECTION_JSPANEL_H + +#include "napi/native_api.h" +#include "selection_panel.h" +#include "async_call.h" +#include "panel_common.h" +#include "panel_info.h" +#include "ffrt_block_queue.h" + +#include +#include + +namespace OHOS { +namespace SelectionFwk { +enum class JsEvent : uint32_t { + RESIZE = 0, + MOVE_TO, + ADJUST_PANEL_RECT, + UPDATE_REGION, + SHOW, + HIDE, + SET_UI_CONTENT, + GET_DISPLAYID, + SET_IMMERSIVE_MODE, + GET_IMMERSIVE_MODE, + START_MOVING, + EVENT_END, +}; + +struct JsEventInfo { + std::chrono::system_clock::time_point timestamp{}; + JsEvent event{ JsEvent::EVENT_END }; + bool operator==(const JsEventInfo &info) const + { + return (timestamp == info.timestamp && event == info.event); + } +}; + +class JsPanel { +public: + JsPanel() = default; + ~JsPanel(); + static napi_value Init(napi_env env); + static napi_value SetUiContent(napi_env env, napi_callback_info info); + static napi_value Show(napi_env env, napi_callback_info info); + static napi_value Hide(napi_env env, napi_callback_info info); + static napi_value StartMoving(napi_env env, napi_callback_info info); + static napi_value MoveTo(napi_env env, napi_callback_info info); + static napi_value Subscribe(napi_env env, napi_callback_info info); + static napi_value UnSubscribe(napi_env env, napi_callback_info info); + void SetNative(const std::shared_ptr &panel); + std::shared_ptr GetNative(); +private: + struct PanelContentContext : public AsyncCall::Context { + LayoutParams layoutParams = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; + EnhancedLayoutParams enhancedLayoutParams; + HotAreas hotAreas; + std::vector hotArea; + bool isEnhancedCall{ false }; + std::string path = ""; + uint32_t width = 0; + uint32_t height = 0; + int32_t x = 0; + int32_t y = 0; + uint64_t displayId = 0; + std::shared_ptr selectionPanel = nullptr; + std::shared_ptr contentStorage = nullptr; + JsEventInfo info; + PanelContentContext(napi_env env, napi_callback_info info) : Context(nullptr, nullptr) + { + napi_value self = nullptr; + napi_status status = napi_get_cb_info(env, info, 0, nullptr, &self, nullptr); + CHECK_RETURN_VOID((status == napi_ok) && (self != nullptr), "get callback info failed."); + void *native = nullptr; + status = napi_unwrap(env, self, &native); + CHECK_RETURN_VOID((status == napi_ok) && (native != nullptr), "get jsPanel failed."); + selectionPanel = reinterpret_cast(native)->GetNative(); + }; + PanelContentContext(InputAction input, OutputAction output) : Context(std::move(input), std::move(output)){}; + napi_status operator()(napi_env env, size_t argc, napi_value *argv, napi_value self) override + { + CHECK_RETURN(self != nullptr, "self is nullptr", napi_invalid_arg); + return Context::operator()(env, argc, argv, self); + } + napi_status operator()(napi_env env, napi_value *result) override + { + if (status_ != napi_ok) { + output_ = nullptr; + return status_; + } + return Context::operator()(env, result); + } + }; + + static napi_value JsNew(napi_env env, napi_callback_info info); + static std::shared_ptr UnwrapPanel(napi_env env, napi_value thisVar); + static void PrintEditorQueueInfoIfTimeout(int64_t start, const JsEventInfo ¤tInfo); + static const std::string CLASS_NAME; + static thread_local napi_ref panelConstructorRef_; + std::shared_ptr selectionPanel_ = nullptr; + static std::mutex panelConstructorMutex_; + static FFRTBlockQueue jsQueue_; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif //SELECTION_JSPANEL_H diff --git a/frameworks/js/napi/selection_ability/js_selection_ability.cpp b/frameworks/js/napi/selection_ability/js_selection_ability.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28db8414eb23c9e0102c18b8cfb8c902f69cc379 --- /dev/null +++ b/frameworks/js/napi/selection_ability/js_selection_ability.cpp @@ -0,0 +1,206 @@ +/* + * 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 "js_selection_ability.h" + +#include "event_checker.h" +#include "selection_js_utils.h" +#include "napi/native_node_api.h" +#include "selection_log.h" +#include "util.h" + +namespace OHOS { +namespace SelectionFwk { +constexpr size_t ARGC_ONE = 1; +constexpr size_t ARGC_TWO = 2; + +const std::string JsSelectionAbility::KDS_CLASS_NAME = "SelectionAbility"; +thread_local napi_ref JsSelectionAbility::KDSRef_ = nullptr; +std::mutex JsSelectionAbility::selectionMutex_; +std::shared_ptr JsSelectionAbility::selectionDelegate_{ nullptr }; + +napi_value JsSelectionAbility::GetSelectionAbility(napi_env env, napi_callback_info info) +{ + SELECTION_HILOGI("GetSelectionAbility"); + return nullptr; +} + +std::shared_ptr JsSelectionAbility::GetJsSelectionAbility() +{ + if (selectionDelegate_ == nullptr) { + std::lock_guard lock(selectionMutex_); + if (selectionDelegate_ == nullptr) { + auto delegate = std::make_shared(); + if (delegate == nullptr) { + SELECTION_HILOGE("keyboard delegate is nullptr!"); + return nullptr; + } + selectionDelegate_ = delegate; + } + } + return selectionDelegate_; +} + +napi_value JsSelectionAbility::JsConstructor(napi_env env, napi_callback_info cbinfo) +{ + napi_value thisVar = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr)); + auto delegate = GetJsSelectionAbility(); + if (delegate == nullptr) { + SELECTION_HILOGE("failed to get delegate!"); + napi_value result = nullptr; + napi_get_null(env, &result); + return result; + } + napi_status status = napi_wrap( + env, thisVar, delegate.get(), [](napi_env env, void *nativeObject, void *hint) {}, nullptr, nullptr); + if (status != napi_ok) { + SELECTION_HILOGE("failed to wrap: %{public}d!", status); + return nullptr; + } + return thisVar; +}; + +napi_value JsSelectionAbility::Subscribe(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_TWO; + napi_value argv[ARGC_TWO] = { nullptr }; + napi_value thisVar = nullptr; + void *data = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)); + std::string type; + // 2 means least param num. + if (argc < 2 || !JsUtil::GetValue(env, argv[0], type) || + !EventChecker::IsValidEventType(EventSubscribeModule::SELECTION_METHOD_ABILITY, type) || + JsUtil::GetType(env, argv[1]) != napi_function) { + SELECTION_HILOGE("subscribe failed, type: %{public}s!", type.c_str()); + return nullptr; + } + SELECTION_HILOGE("subscribe type: %{public}s.", type.c_str()); + auto selectionAbility = reinterpret_cast(JsUtils::GetNativeSelf(env, info)); + if (selectionAbility == nullptr) { + return nullptr; + } + std::shared_ptr callback = + std::make_shared(env, argv[1], std::this_thread::get_id(), + AppExecFwk::EventHandler::Current()); + selectionAbility->RegisterListener(argv[ARGC_ONE], type, callback); + + napi_value result = nullptr; + napi_get_null(env, &result); + return result; +} + + +napi_value JsSelectionAbility::UnSubscribe(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_TWO; + napi_value argv[ARGC_TWO] = { nullptr }; + napi_value thisVar = nullptr; + void *data = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)); + std::string type; + // 1 means least param num. + if (argc < 1 || !JsUtil::GetValue(env, argv[0], type) || + !EventChecker::IsValidEventType(EventSubscribeModule::SELECTION_METHOD_ABILITY, type)) { + SELECTION_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str()); + return nullptr; + } + + // if the second param is not napi_function/napi_null/napi_undefined, return + auto paramType = JsUtil::GetType(env, argv[1]); + if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) { + return nullptr; + } + // if the second param is napi_function, delete it, else delete all + argv[1] = paramType == napi_function ? argv[1] : nullptr; + + SELECTION_HILOGD("unsubscribe type: %{public}s.", type.c_str()); + auto delegate = reinterpret_cast(JsUtils::GetNativeSelf(env, info)); + if (delegate == nullptr) { + return nullptr; + } + delegate->UnRegisterListener(argv[ARGC_ONE], type); + napi_value result = nullptr; + napi_get_null(env, &result); + return result; +} + +void JsSelectionAbility::RegisterListener(napi_value callback, std::string type, + std::shared_ptr callbackObj) +{ + SELECTION_HILOGD("RegisterListener %{public}s", type.c_str()); + std::lock_guard lock(mutex_); + if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) { + SELECTION_HILOGD("methodName %{public}s is not registered!", type.c_str()); + } + auto callbacks = jsCbMap_[type]; + bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr cb) { + return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_); + }); + if (ret) { + SELECTION_HILOGD("JsSelectionAbility callback already registered!"); + return; + } + + SELECTION_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str()); + jsCbMap_[type].push_back(std::move(callbackObj)); +} + +void JsSelectionAbility::UnRegisterListener(napi_value callback, std::string type) +{ + SELECTION_HILOGI("unregister listener: %{public}s.", type.c_str()); + std::lock_guard lock(mutex_); + if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) { + SELECTION_HILOGE("methodName %{public}s is not unregistered!", type.c_str()); + return; + } + + if (callback == nullptr) { + jsCbMap_.erase(type); + SELECTION_HILOGE("callback is nullptr!"); + return; + } + + for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) { + if ((callback != nullptr) && + (JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_))) { + jsCbMap_[type].erase(item); + break; + } + } + if (jsCbMap_[type].empty()) { + jsCbMap_.erase(type); + } +} + +napi_value JsSelectionAbility::Init(napi_env env, napi_value exports) +{ + SELECTION_HILOGI("napi init"); + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("getSelectionAbility", GetSelectionAbility), + DECLARE_NAPI_FUNCTION("on", Subscribe), + DECLARE_NAPI_FUNCTION("off", UnSubscribe), + }; + + napi_value cons = nullptr; + NAPI_CALL(env, napi_define_class(env, KDS_CLASS_NAME.c_str(), KDS_CLASS_NAME.size(), JsConstructor, nullptr, + sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons)); + NAPI_CALL(env, napi_create_reference(env, cons, 1, &KDSRef_)); + NAPI_CALL(env, napi_set_named_property(env, exports, KDS_CLASS_NAME.c_str(), cons)); + return exports; +} +} // namespace OHOS::SelectionFwk +} // namespace OHOS diff --git a/frameworks/js/napi/selection_ability/js_selection_engine_setting.cpp b/frameworks/js/napi/selection_ability/js_selection_engine_setting.cpp new file mode 100644 index 0000000000000000000000000000000000000000..90f2d24f41eda5800d6c47095177a78f5375b519 --- /dev/null +++ b/frameworks/js/napi/selection_ability/js_selection_engine_setting.cpp @@ -0,0 +1,500 @@ +/* + * 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 "js_selection_engine_setting.h" + +#include "event_checker.h" +#include "iservice_registry.h" +#include "selection_ability.h" +#include "selection_js_utils.h" +#include "callback_handler.h" +#include "napi/native_node_api.h" +#include "selection_listener_impl.h" +#include "selection_log.h" +#include "system_ability_definition.h" +#include "util.h" +#include "napi_base_context.h" +#include "js_panel.h" +#include "js_selection_utils.h" +#include "selection_app_validator.h" +#include "selection_api_event_reporter.h" + +using namespace OHOS; +namespace OHOS { +namespace SelectionFwk { + +constexpr size_t ARGC_ONE = 1; +constexpr size_t ARGC_TWO = 2; + +const std::string JsSelectionEngineSetting::KDS_CLASS_NAME = "SelectionAbility"; +thread_local napi_ref JsSelectionEngineSetting::KDSRef_ = nullptr; +std::mutex JsSelectionEngineSetting::selectionMutex_; +std::shared_ptr JsSelectionEngineSetting::selectionDelegate_{ nullptr }; +std::mutex JsSelectionEngineSetting::eventHandlerMutex_; +std::shared_ptr JsSelectionEngineSetting::handler_{ nullptr }; +sptr JsSelectionEngineSetting::listenerStub_ { nullptr }; +sptr JsSelectionEngineSetting::abilityManager_ { nullptr }; + +napi_value JsSelectionEngineSetting::Subscribe(napi_env env, napi_callback_info info) +{ + if (!SelectionAppValidator::GetInstance().Validate()) { + JsUtils::ThrowException(env, JsUtils::Convert(ErrorCode::ERROR_INVALID_OPERATION), + "BundleName is invalid", TYPE_NONE); + return nullptr; + } + size_t argc = ARGC_TWO; + napi_value argv[ARGC_TWO] = { nullptr }; + napi_value thisVar = nullptr; + void *data = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)); + std::string type; + // 2 means least param num. + if (argc < 2 || !JsUtil::GetValue(env, argv[0], type) || + !EventChecker::IsValidEventType(EventSubscribeModule::SELECTION_METHOD_ABILITY, type) || + JsUtil::GetType(env, argv[1]) != napi_function) { + SELECTION_HILOGE("subscribe failed, type: %{public}s!", type.c_str()); + JsUtils::ThrowException(env, SFErrorCode::EXCEPTION_PARAMCHECK, "", TYPE_NONE); + return nullptr; + } + SELECTION_HILOGI("subscribe type: %{public}s.", type.c_str()); + + std::shared_ptr callback = + std::make_shared(env, argv[1], std::this_thread::get_id(), + AppExecFwk::EventHandler::Current()); + auto engine = JsSelectionEngineSetting::GetJsSelectionEngineSetting(); + engine->RegisterListener(argv[ARGC_ONE], type, callback); + napi_value result = nullptr; + napi_get_null(env, &result); + return result; +} + + +napi_value JsSelectionEngineSetting::UnSubscribe(napi_env env, napi_callback_info info) +{ + size_t argc = ARGC_TWO; + napi_value argv[ARGC_TWO] = { nullptr }; + napi_value thisVar = nullptr; + void *data = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)); + std::string type; + // 1 means least param num. + if (argc < 1 || !JsUtil::GetValue(env, argv[0], type) || + !EventChecker::IsValidEventType(EventSubscribeModule::SELECTION_METHOD_ABILITY, type)) { + SELECTION_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str()); + JsUtils::ThrowException(env, SFErrorCode::EXCEPTION_PARAMCHECK, "", TYPE_NONE); + return nullptr; + } + + // if the second param is not napi_function/napi_null/napi_undefined, return + auto paramType = JsUtil::GetType(env, argv[1]); + if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) { + return nullptr; + } + // if the second param is napi_function, delete it, else delete all + argv[1] = paramType == napi_function ? argv[1] : nullptr; + + SELECTION_HILOGD("unsubscribe type: %{public}s.", type.c_str()); + auto delegate = reinterpret_cast(JsUtils::GetNativeSelf(env, info)); + if (delegate == nullptr) { + return nullptr; + } + delegate->UnRegisterListener(argv[ARGC_ONE], type); + napi_value result = nullptr; + napi_get_null(env, &result); + return result; +} + +napi_status JsSelectionEngineSetting::GetContext(napi_env env, napi_value in, + std::shared_ptr &context) +{ + bool stageMode = false; + napi_status status = OHOS::AbilityRuntime::IsStageContext(env, in, stageMode); + if (status != napi_ok || (!stageMode)) { + SELECTION_HILOGE("it's not in stage mode."); + return status; + } + context = OHOS::AbilityRuntime::GetStageModeContext(env, in); + if (context == nullptr) { + SELECTION_HILOGE("context is nullptr."); + return napi_generic_failure; + } + return napi_ok; +} + +napi_value JsSelectionEngineSetting::CreatePanel(napi_env env, napi_callback_info info) +{ + SELECTION_HILOGI("SelectionEngineSetting CreatePanel start."); + auto eventReporter = std::make_shared("createPanel"); + auto ctxt = std::make_shared(); + auto inputInner = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + PARAM_CHECK_RETURN(env, argc >= 2, "at least two parameters is required.", TYPE_NONE, napi_invalid_arg); + napi_valuetype valueType = napi_undefined; + // 0 means parameter of ctx + napi_typeof(env, argv[0], &valueType); + PARAM_CHECK_RETURN(env, valueType == napi_object, "ctx type must be BaseContext.", TYPE_NONE, napi_invalid_arg); + napi_status status = GetContext(env, argv[0], ctxt->context); + PARAM_CHECK_RETURN(env, status == napi_ok, "js param context covert failed.", TYPE_NONE, napi_invalid_arg); + // 1 means parameter of info + napi_typeof(env, argv[1], &valueType); + PARAM_CHECK_RETURN(env, valueType == napi_object, "param info type must be PanelInfo.", TYPE_NONE, + napi_invalid_arg); + status = OHOS::SelectionFwk::JsSelectionUtils::GetValue(env, argv[1], ctxt->panelInfo); + SELECTION_HILOGD("output js param panelInfo covert , panelType/x/y/width/height: \ + %{public}d/%{public}d/%{public}d/%{public}d/%{public}d.", static_cast(ctxt->panelInfo.panelType), + ctxt->panelInfo.x, ctxt->panelInfo.y, ctxt->panelInfo.width, ctxt->panelInfo.height); + PARAM_CHECK_RETURN(env, status == napi_ok, "js param info covert failed!", TYPE_NONE, napi_invalid_arg); + PARAM_CHECK_RETURN(env, ctxt->panelInfo.x >= 0 && ctxt->panelInfo.y >= 0 && ctxt->panelInfo.width > 0 && + ctxt->panelInfo.height > 0, "js param is invalid: x/y cannot be negative, width/height must be positive!", + TYPE_NONE, napi_invalid_arg); + return status; + }; + auto input = [=](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + napi_status status = inputInner(env, argc, argv, self); + if (status != napi_ok) { + SELECTION_HILOGI("Check parameter invalid, Report error message to hiappevent"); + eventReporter->WriteEndEvent(SelectionApiEventReporter::API_FAIL, SFErrorCode::EXCEPTION_PARAMCHECK); + } + return status; + }; + + auto exec = [eventReporter, ctxt](AsyncCall::Context *ctx) { + auto ret = SelectionAbility::GetInstance()->CreatePanel(ctxt->context, ctxt->panelInfo, ctxt->panel); + if (ret != ErrorCode::NO_ERROR) { + ctxt->SetErrorCode(ret); + eventReporter->WriteEndEvent(SelectionApiEventReporter::API_FAIL, JsUtils::Convert(ret)); + return; + } + ctxt->SetState(napi_ok); + eventReporter->WriteEndEvent(SelectionApiEventReporter::API_SUCCESS, ret); + }; + + auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { + JsPanel *jsPanel = nullptr; + napi_value constructor = JsPanel::Init(env); + CHECK_RETURN(constructor != nullptr, "failed to get panel constructor!", napi_generic_failure); + + napi_status status = napi_new_instance(env, constructor, 0, nullptr, result); + CHECK_RETURN(status == napi_ok, "jsPanel new instance failed!", napi_generic_failure); + + status = napi_unwrap(env, *result, (void **)(&jsPanel)); + CHECK_RETURN((status == napi_ok) && (jsPanel != nullptr), "get jsPanel unwrap failed!", napi_generic_failure); + jsPanel->SetNative(ctxt->panel); + return napi_ok; + }; + + ctxt->SetAction(std::move(input), std::move(output)); + // 3 means JsAPI:createPanel has 3 params at most. + AsyncCall asyncCall(env, info, ctxt, 3); + return asyncCall.Call(env, exec, "createPanel"); +} + +napi_value JsSelectionEngineSetting::DestroyPanel(napi_env env, napi_callback_info info) +{ + SELECTION_HILOGI("SelectionEngineSetting DestroyPanel start."); + auto ctxt = std::make_shared(); + auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, napi_invalid_arg); + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[0], &valueType); + PARAM_CHECK_RETURN(env, valueType == napi_object, "param panel type must be SelectionPanel.", TYPE_NONE, + napi_invalid_arg); + bool isPanel = false; + napi_value constructor = JsPanel::Init(env); + CHECK_RETURN(constructor != nullptr, "failed to get panel constructor.", napi_invalid_arg); + napi_status status = napi_instanceof(env, argv[0], constructor, &isPanel); + CHECK_RETURN((status == napi_ok) && isPanel, "param verification failed, it's not expected panel instance!", + napi_invalid_arg); + JsPanel *jsPanel = nullptr; + status = napi_unwrap(env, argv[0], (void **)(&jsPanel)); + CHECK_RETURN((status == napi_ok) && (jsPanel != nullptr), "failed to unwrap JsPanel!", napi_invalid_arg); + ctxt->panel = jsPanel->GetNative(); + CHECK_RETURN((ctxt->panel != nullptr), "panel is nullptr!", napi_invalid_arg); + return status; + }; + + auto exec = [ctxt](AsyncCall::Context *ctx) { ctxt->SetState(napi_ok); }; + + auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { + CHECK_RETURN((ctxt->panel != nullptr), "panel is nullptr!", napi_generic_failure); + auto errCode = SelectionAbility::GetInstance()->DestroyPanel(ctxt->panel); + if (errCode != ErrorCode::NO_ERROR) { + SELECTION_HILOGE("DestroyPanel failed, errCode: %{public}d!", JsUtils::Convert(errCode)); + ctxt->SetErrorCode(errCode); + return napi_generic_failure; + } + ctxt->panel = nullptr; + return napi_ok; + }; + + ctxt->SetAction(std::move(input), std::move(output)); + // 2 means JsAPI:destroyPanel has 2 params at most. + AsyncCall asyncCall(env, info, ctxt, 2); + return asyncCall.Call(env, exec, "destroyPanel"); +} + +sptr JsSelectionEngineSetting::GetSelectionSystemAbility() +{ + auto systemAbilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (systemAbilityManager == nullptr) { + SELECTION_HILOGE("system ability manager is nullptr!"); + return nullptr; + } + + sptr systemAbility = nullptr; + systemAbility = systemAbilityManager->GetSystemAbility(SELECTION_FWK_SA_ID); + if (systemAbility == nullptr) { + SELECTION_HILOGE("get system ability is nullptr!"); + return nullptr; + } + + abilityManager_ = iface_cast(systemAbility); + return abilityManager_; +} + +void JsSelectionEngineSetting::RegisterListener(napi_value callback, std::string type, + std::shared_ptr callbackObj) +{ + SELECTION_HILOGI("RegisterListener %{public}s", type.c_str()); + std::lock_guard lock(mutex_); + if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) { + SELECTION_HILOGI("methodName %{public}s is not registered!", type.c_str()); + } + auto callbacks = jsCbMap_[type]; + bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr cb) { + return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_); + }); + if (ret) { + SELECTION_HILOGI("JsSelectionEngineSetting callback already registered!"); + return; + } + + SELECTION_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str()); + jsCbMap_[type].push_back(std::move(callbackObj)); +} + +void JsSelectionEngineSetting::UnRegisterListener(napi_value callback, std::string type) +{ + SELECTION_HILOGI("unregister listener: %{public}s.", type.c_str()); + std::lock_guard lock(mutex_); + if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) { + SELECTION_HILOGE("methodName %{public}s is not unregistered!", type.c_str()); + return; + } + + if (callback == nullptr) { + jsCbMap_.erase(type); + SELECTION_HILOGE("callback is nullptr!"); + return; + } + + for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) { + if ((callback != nullptr) && + (JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_))) { + jsCbMap_[type].erase(item); + break; + } + } + if (jsCbMap_[type].empty()) { + jsCbMap_.erase(type); + } + + auto proxy = GetSelectionSystemAbility(); + if (proxy == nullptr || listenerStub_ == nullptr) { + SELECTION_HILOGE("selection system ability or listenerStub_ is nullptr!"); + return; + } + proxy->UnregisterListener(listenerStub_); + listenerStub_ = nullptr; +} + +std::shared_ptr JsSelectionEngineSetting::GetJsSelectionEngineSetting() +{ + if (selectionDelegate_ == nullptr) { + std::lock_guard lock(selectionMutex_); + if (selectionDelegate_ == nullptr) { + std::shared_ptr delegate(new JsSelectionEngineSetting); + if (delegate == nullptr) { + SELECTION_HILOGE("JsSelectionEngineSetting is nullptr!"); + return nullptr; + } + selectionDelegate_ = delegate; + } + } + { + std::lock_guard lock(eventHandlerMutex_); + handler_ = AppExecFwk::EventHandler::Current(); + } + return selectionDelegate_; +} + +SFErrorCode JsSelectionEngineSetting::RegisterListenerToService( + std::shared_ptr &selectionEnging) +{ + auto proxy = GetSelectionSystemAbility(); + if (proxy == nullptr) { + SELECTION_HILOGE("selection system ability is nullptr!"); + return EXCEPTION_SELECTION_SERVICE; + } + listenerStub_ = new (std::nothrow) SelectionListenerImpl(selectionEnging); + if (listenerStub_ == nullptr) { + SELECTION_HILOGE("Failed to create SelectionListenerImpl instance."); + return EXCEPTION_SELECTION_SERVICE; + } + SELECTION_HILOGI("Begin calling SA RegisterListener!"); + if (proxy->RegisterListener(listenerStub_) != ERR_OK) { + return EXCEPTION_SELECTION_SERVICE; + } + + return EXCEPTION_SUCCESS; +} + +SFErrorCode JsSelectionEngineSetting::Register(napi_env env) +{ + auto delegate = GetJsSelectionEngineSetting(); + if (delegate == nullptr) { + SELECTION_HILOGE("failed to get delegate!"); + return EXCEPTION_SELECTION_SERVICE; + } + + return RegisterListenerToService(delegate); +}; + +napi_value JsSelectionEngineSetting::Init(napi_env env, napi_value exports) +{ + SELECTION_HILOGI("napi init"); + napi_property_descriptor descriptor[] = { + DECLARE_NAPI_FUNCTION("on", Subscribe), + DECLARE_NAPI_FUNCTION("off", UnSubscribe), + DECLARE_NAPI_FUNCTION("createPanel", CreatePanel), + DECLARE_NAPI_FUNCTION("destroyPanel", DestroyPanel), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(descriptor) / sizeof(napi_property_descriptor), + descriptor)); + NAPI_CALL(env, napi_set_named_property(env, exports, "SelectionType", GetJsSelectionTypeProperty(env))); + if (Register(env) != EXCEPTION_SUCCESS) { + SELECTION_HILOGE("regist lister to service failed!"); + return nullptr; + } + SelectionAppValidator::GetInstance().SetValid(); + return exports; +} + +napi_value JsSelectionEngineSetting::GetJsSelectionTypeProperty(napi_env env) +{ + napi_value obj = nullptr; + NAPI_CALL(env, napi_create_object(env, &obj)); + + auto ret = JsUtil::Object::WriteProperty(env, obj, "MOUSE_MOVE", static_cast(MOVE_SELECTION)); + if (!ret) { + SELECTION_HILOGE("Failed to init module selectionInput.selectionManager.SelectionType as MOUSE_MOVE"); + return nullptr; + } + ret = JsUtil::Object::WriteProperty(env, obj, "DOUBLE_CLICK", static_cast(DOUBLE_CLICKED_SELECTION)); + if (!ret) { + SELECTION_HILOGE("Failed to init module selectionInput.selectionManager.SelectionType as DOUBLE_CLICK"); + return nullptr; + } + ret = JsUtil::Object::WriteProperty(env, obj, "TRIPLE_CLICK", static_cast(TRIPLE_CLICKED_SELECTION)); + if (!ret) { + SELECTION_HILOGE("Failed to init module selectionInput.selectionManager.SelectionType as TRIPLE_CLICK"); + return nullptr; + } + return obj; +} + +std::shared_ptr JsSelectionEngineSetting::GetEventHandler() +{ + std::lock_guard lock(eventHandlerMutex_); + return handler_; +} + +std::shared_ptr JsSelectionEngineSetting::GetEntry(const std::string &type, + EntrySetter entrySetter) +{ + SELECTION_HILOGI("start, type: %{public}s", type.c_str()); + std::shared_ptr entry = nullptr; + { + std::lock_guard lock(mutex_); + if (jsCbMap_[type].empty()) { + SELECTION_HILOGI("%{public}s cb-vector is empty.", type.c_str()); + return nullptr; + } + entry = std::make_shared(jsCbMap_[type], type); + } + if (entrySetter != nullptr) { + entrySetter(*entry); + } + return entry; +} + +napi_value JsSelectionEngineSetting::Write(napi_env env, const SelectionInfo &selectionInfo) +{ + napi_value jsObject = nullptr; + napi_create_object(env, &jsObject); + auto ret = JsUtil::Object::WriteProperty(env, jsObject, "bundleName", selectionInfo.bundleName); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "windowID", selectionInfo.windowId); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "displayID", selectionInfo.displayId); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "endWindowY", selectionInfo.endWindowY); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "endWindowX", selectionInfo.endWindowX); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "startWindowY", selectionInfo.startWindowY); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "startWindowX", selectionInfo.startWindowX); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "endDisplayY", selectionInfo.endDisplayY); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "endDisplayX", selectionInfo.endDisplayX); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "startDisplayY", selectionInfo.startDisplayY); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "startDisplayX", selectionInfo.startDisplayX); + ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "text", selectionInfo.text); + SELECTION_HILOGD("write selectionInfo into object, ret=%{public}s", ret ? "true" : "false"); + return ret ? jsObject : JsUtil::Const::Null(env); +} + +int32_t JsSelectionEngineSetting::OnSelectionEvent(const SelectionInfo &selectionInfo) +{ + SELECTION_HILOGD("OnSelectionEvent begin"); + std::string type = "selectionCompleted"; + auto entry = GetEntry(type, [&selectionInfo](SelectionEntry &entry) {entry.selectionInfo = selectionInfo; }); + if (entry == nullptr) { + SELECTION_HILOGE("failed to get SelectionEntry entry!"); + return 1; + } + + auto eventHandler = GetEventHandler(); + if (eventHandler == nullptr) { + SELECTION_HILOGE("eventHandler is nullptr!"); + return 1; + } + + SELECTION_HILOGI("selection text length is [%{public}u]", entry->selectionInfo.text.length()); + auto task = [entry]() { + auto paramGetter = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool { + if (argc == 0) { + return false; + } + napi_value jsObject = Write(env, entry->selectionInfo); + if (jsObject == JsUtil::Const::Null(env)) { + SELECTION_HILOGE("jsObject is nullptr!"); + return false; + } + // 0 means the first param of callback. + args[0] = jsObject; + return true; + }; + // 1 means callback has one param. + JsCallbackHandler::Traverse(entry->vecCopy, { 1, paramGetter }); + }; + eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP); + return 0; +} +} +} diff --git a/frameworks/js/napi/selection_ability/js_selection_engine_setting.h b/frameworks/js/napi/selection_ability/js_selection_engine_setting.h new file mode 100644 index 0000000000000000000000000000000000000000..c1661c39b12e703a60f6cc4af9e113baa6693eab --- /dev/null +++ b/frameworks/js/napi/selection_ability/js_selection_engine_setting.h @@ -0,0 +1,111 @@ +/* + * 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 JS_SELECTION_ENGINE_SETTING_H +#define JS_SELECTION_ENGINE_SETTING_H + +#include +#include +#include +#include + +#include "callback_object.h" +#include "iremote_stub.h" +#include "iselection_listener.h" +#include "iselection_service.h" +#include "napi/native_api.h" +#include "refbase.h" +#include "selection_interface.h" +#include "util.h" +#include "async_call.h" +#include "selection_panel.h" +#include "panel_info.h" + +namespace OHOS { +namespace SelectionFwk { +class JsSelectionEngineSetting : public SelectionInterface { +public: + static napi_value Init(napi_env env, napi_value exports); + static napi_value InitProperty(napi_env env, napi_value exports); + static napi_value GetSelectionAbility(napi_env env, napi_callback_info info); + static napi_value Subscribe(napi_env env, napi_callback_info info); + static napi_value UnSubscribe(napi_env env, napi_callback_info info); + static napi_value CreatePanel(napi_env env, napi_callback_info info); + static napi_value DestroyPanel(napi_env env, napi_callback_info info); + int32_t OnSelectionEvent(const SelectionInfo &selectionInfo); + +private: + struct SelectionEntry { + std::vector> vecCopy; + std::string type; + SelectionInfo selectionInfo; + SelectionEntry(const std::vector> &cbVec, const std::string &type) + : vecCopy(cbVec), type(type) + { + } + }; + + struct PanelContext : public AsyncCall::Context { + PanelInfo panelInfo = PanelInfo(); + std::shared_ptr panel = nullptr; + std::shared_ptr context = nullptr; + PanelContext() : Context(nullptr, nullptr){}; + PanelContext(InputAction input, OutputAction output) : Context(std::move(input), std::move(output)){}; + + napi_status operator()(napi_env env, size_t argc, napi_value *argv, napi_value self) override + { + CHECK_RETURN(self != nullptr, "self is nullptr", napi_invalid_arg); + return Context::operator()(env, argc, argv, self); + } + napi_status operator()(napi_env env, napi_value *result) override + { + if (status_ != napi_ok) { + output_ = nullptr; + return status_; + } + return Context::operator()(env, result); + } + }; + + JsSelectionEngineSetting() = default; + static napi_value GetSEInstance(napi_env env, napi_callback_info info); + static SFErrorCode Register(napi_env env); + static napi_value Write(napi_env env, const SelectionInfo &selectionInfo); + static napi_status GetContext(napi_env env, napi_value in, std::shared_ptr &context); + static std::shared_ptr GetJsSelectionEngineSetting(); + void RegisterListener(napi_value callback, std::string type, std::shared_ptr callbackObj); + void UnRegisterListener(napi_value callback, std::string type); + static sptr GetSelectionSystemAbility(); + static SFErrorCode RegisterListenerToService(std::shared_ptr &selectionEnging); + static std::shared_ptr GetEventHandler(); + using EntrySetter = std::function; + std::shared_ptr GetEntry(const std::string &type, EntrySetter entrySetter = nullptr); + static napi_value GetJsSelectionTypeProperty(napi_env env); + +private: + static const std::string KDS_CLASS_NAME; + static thread_local napi_ref KDSRef_; + std::map>> jsCbMap_; + static std::mutex selectionMutex_; + static std::shared_ptr selectionDelegate_; + std::recursive_mutex mutex_; + static sptr listenerStub_; + static sptr abilityManager_; + static std::mutex eventHandlerMutex_; + static std::shared_ptr handler_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif //JS_SELECTION_ENGINE_SETTING_H diff --git a/frameworks/js/napi/selection_ability/panel_listener_impl.cpp b/frameworks/js/napi/selection_ability/panel_listener_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..828b729418d9759b76bdbca0863a53aaa9cfe062 --- /dev/null +++ b/frameworks/js/napi/selection_ability/panel_listener_impl.cpp @@ -0,0 +1,167 @@ +/* + * 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 "panel_listener_impl.h" + +#include "selection_js_utils.h" +#include "callback_handler.h" +#include "util.h" + +namespace OHOS { +namespace SelectionFwk { +std::shared_ptr PanelListenerImpl::instance_{ nullptr }; +std::mutex PanelListenerImpl::listenerMutex_; +std::shared_ptr PanelListenerImpl::GetInstance() +{ + if (instance_ == nullptr) { + std::lock_guard lock(listenerMutex_); + if (instance_ == nullptr) { + instance_ = std::make_shared(); + } + } + return instance_; +} + +PanelListenerImpl::~PanelListenerImpl() {} + +void PanelListenerImpl::SetEventHandler(std::shared_ptr handler) +{ + std::unique_lock lock(eventHandlerMutex_); + handler_ = handler; +} + +std::shared_ptr PanelListenerImpl::GetEventHandler() +{ + std::shared_lock lock(eventHandlerMutex_); + return handler_; +} + +void PanelListenerImpl::OnPanelStatus(uint32_t windowId, const std::string& status) +{ + auto eventHandler = GetEventHandler(); + if (eventHandler == nullptr) { + SELECTION_HILOGE("eventHandler is nullptr!"); + return; + } + CallbackVector callBacks = GetCallback(windowId, status); + if (callBacks.empty()) { + SELECTION_HILOGE("callBacks is empty!"); + return; + } + SELECTION_HILOGI("OnPanelStatus status: %{public}s", status.c_str()); + auto task = [callBacks]() { + SelectionFwk::JsCallbackHandler::Traverse(callBacks); + }; + eventHandler->PostTask(task, status, 0, AppExecFwk::EventQueue::Priority::VIP); +} + +CallbackVector PanelListenerImpl::GetCallback(uint32_t windowId, const std::string &type) +{ + CallbackVector callBackVector; + callbacks_.ComputeIfPresent(windowId, [&type, &callBackVector](uint32_t id, TypeMap& callbacks) { + if (callbacks.find(type) != callbacks.end()) { + callBackVector = callbacks[type]; + } + return !callbacks.empty(); + }); + return callBackVector; +} + +napi_value JsWindowSize::Write(napi_env env, const WindowSize &nativeObject) +{ + napi_value jsObject = nullptr; + napi_create_object(env, &jsObject); + bool ret = SelectionFwk::JsUtil::Object::WriteProperty(env, jsObject, "width", nativeObject.width); + ret = ret && SelectionFwk::JsUtil::Object::WriteProperty(env, jsObject, "height", nativeObject.height); + return ret ? jsObject : SelectionFwk::JsUtil::Const::Null(env); +} + +bool JsWindowSize::Read(napi_env env, napi_value jsObject, WindowSize &nativeObject) +{ + auto ret = SelectionFwk::JsUtil::Object::ReadProperty(env, jsObject, "width", nativeObject.width); + ret = ret && SelectionFwk::JsUtil::Object::ReadProperty(env, jsObject, "height", nativeObject.height); + return ret; +} + +napi_value JsKeyboardArea::Write(napi_env env, const PanelAdjustInfo &nativeObject) +{ + napi_value jsObject = nullptr; + napi_create_object(env, &jsObject); + bool ret = SelectionFwk::JsUtil::Object::WriteProperty(env, jsObject, "top", nativeObject.top); + ret = ret && SelectionFwk::JsUtil::Object::WriteProperty(env, jsObject, "bottom", nativeObject.bottom); + ret = ret && SelectionFwk::JsUtil::Object::WriteProperty(env, jsObject, "left", nativeObject.left); + ret = ret && SelectionFwk::JsUtil::Object::WriteProperty(env, jsObject, "right", nativeObject.right); + return ret ? jsObject : SelectionFwk::JsUtil::Const::Null(env); +} + +bool JsKeyboardArea::Read(napi_env env, napi_value jsObject, PanelAdjustInfo &nativeObject) +{ + bool ret = SelectionFwk::JsUtil::Object::ReadProperty(env, jsObject, "top", nativeObject.top); + ret = ret && SelectionFwk::JsUtil::Object::ReadProperty(env, jsObject, "bottom", nativeObject.bottom); + ret = ret && SelectionFwk::JsUtil::Object::ReadProperty(env, jsObject, "left", nativeObject.left); + ret = ret && SelectionFwk::JsUtil::Object::ReadProperty(env, jsObject, "right", nativeObject.right); + return ret; +} + +void PanelListenerImpl::Subscribe(uint32_t windowId, const std::string &type, + std::shared_ptr cbObject) +{ + callbacks_.Compute(windowId, [cbObject, &type](auto windowId, TypeMap& cbs) { + if (cbs.find(type) == cbs.end()) { + cbs.try_emplace(type, CallbackVector {cbObject}); + return !cbs.empty(); + } + auto it = std::find_if( + cbs[type].begin(), cbs[type].end(), + [cbObject](const std::shared_ptr& vecCbObject) { return *vecCbObject == *cbObject; }); + if (it == cbs[type].end()) { + cbs[type].emplace_back(cbObject); + SELECTION_HILOGI("start to subscribe type: %{public}s of windowId: %{public}u.", type.c_str(), windowId); + } else { + SELECTION_HILOGI("type: %{public}s of windowId: %{public}u already subscribed.", type.c_str(), windowId); + } + return !cbs.empty(); + }); +} + +void PanelListenerImpl::RemoveInfo(const std::string &type, uint32_t windowId) +{ + callbacks_.ComputeIfPresent(windowId, [&type](auto windowId, TypeMap& cbs) { + cbs.erase(type); + return !cbs.empty(); + }); +} + +void PanelListenerImpl::RemoveInfo(const std::string &type, uint32_t windowId, + std::shared_ptr cbObject) +{ + callbacks_.ComputeIfPresent(windowId, [&type, cbObject](auto windowId, TypeMap& cbs) { + auto it = cbs.find(type); + if (it == cbs.end()) { + return !cbs.empty(); + } + auto targetCallback = std::find_if( + cbs[type].begin(), cbs[type].end(), + [cbObject](std::shared_ptr vecCbObject) { return *vecCbObject == *cbObject; }); + if (targetCallback != cbs[type].end()) { + cbs[type].erase(targetCallback); + } + + return !cbs.empty(); + }); +} + +} // namespace SelectionFwk +} // namespace OHOS diff --git a/frameworks/js/napi/selection_ability/panel_listener_impl.h b/frameworks/js/napi/selection_ability/panel_listener_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..165640416f971343c5c99ee39242e977531631ca --- /dev/null +++ b/frameworks/js/napi/selection_ability/panel_listener_impl.h @@ -0,0 +1,83 @@ +/* + * 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 SELECTION_PANEL_LISTENER_IMPL_H +#define SELECTION_PANEL_LISTENER_IMPL_H + +#include +#include +#include +#include +#include + +#include "concurrent_map.h" +#include "event_handler.h" +#include "selection_panel.h" +#include "callback_object.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "panel_status_listener.h" + + +namespace OHOS { +namespace SelectionFwk { +using CallbackVector = std::vector>; +using TypeMap = std::map; + +struct JsWindowSize { + static napi_value Write(napi_env env, const WindowSize &nativeObject); + static bool Read(napi_env env, napi_value jsObject, WindowSize &nativeObject); +}; + +struct JsKeyboardArea { + static napi_value Write(napi_env env, const PanelAdjustInfo &nativeObject); + static bool Read(napi_env env, napi_value jsObject, PanelAdjustInfo &nativeObject); +}; + +class PanelListenerImpl : public PanelStatusListener { +public: + +struct UvEntry { + WindowSize size; + PanelAdjustInfo keyboardArea; + std::shared_ptr cbCopy; + explicit UvEntry(const std::shared_ptr &cb) : cbCopy(cb) + { + } + }; + + static std::shared_ptr GetInstance(); + ~PanelListenerImpl(); + + void SetEventHandler(std::shared_ptr handler); + std::shared_ptr GetEventHandler(); + + void OnPanelStatus(uint32_t windowId, const std::string& status) override; + void Subscribe(uint32_t windowId, const std::string &type, std::shared_ptr cbObject); + void RemoveInfo(const std::string &type, uint32_t windowId, std::shared_ptr cbObject); + void RemoveInfo(const std::string &type, uint32_t windowId); + + CallbackVector GetCallback(uint32_t windowId, const std::string &type); + + static std::mutex listenerMutex_; + static std::shared_ptr instance_; + mutable std::shared_mutex eventHandlerMutex_; + std::shared_ptr handler_; + ConcurrentMap callbacks_; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif //SELECTION_PANEL_LISTENER_IMPL_H diff --git a/frameworks/js/napi/selection_ability/selection_engine_module.cpp b/frameworks/js/napi/selection_ability/selection_engine_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..55d70ec4bd741949e11e66c5015ed991dda3dc66 --- /dev/null +++ b/frameworks/js/napi/selection_ability/selection_engine_module.cpp @@ -0,0 +1,48 @@ +/* + * 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 "js_selection_engine_setting.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" + +EXTERN_C_START +/* + * function for module exports + */ +static napi_value Init(napi_env env, napi_value exports) +{ + OHOS::SelectionFwk::JsSelectionEngineSetting::Init(env, exports); + return exports; +} +EXTERN_C_END + +/* + * module define + */ +static napi_module _module = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "selectionInput.selectionManager", + .nm_priv = ((void *)0), + .reserved = { 0 } }; +/* + * module register + */ +extern "C" __attribute__((constructor)) void Register() +{ + napi_module_register(&_module); +} \ No newline at end of file diff --git a/frameworks/js/napi/selection_client/async_call.cpp b/frameworks/js/napi/selection_client/async_call.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b11951a596d755bab0a20a52a144d6a35c41a26 --- /dev/null +++ b/frameworks/js/napi/selection_client/async_call.cpp @@ -0,0 +1,244 @@ +/* + * 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 "async_call.h" +#include +#include +#include +#include "selection_log.h" +#include "selection_js_utils.h" +#include "napi/native_node_api.h" + +namespace OHOS { +namespace SelectionFwk { +using namespace std::chrono; +constexpr size_t ARGC_MAX = 6; +constexpr int32_t MAX_WAIT_TIME = 100; // ms +static inline uint64_t GetTimeStamp() +{ + return duration_cast(system_clock::now().time_since_epoch()).count(); +} +AsyncCall::AsyncCall(napi_env env, napi_callback_info info, std::shared_ptr context, size_t maxParamCount) + : env_(env) +{ + context_ = new AsyncContext(); + NAPI_ASSERT_RETURN_VOID(env, context_ != nullptr, "context_ != nullptr"); + size_t argc = ARGC_MAX; + napi_value self = nullptr; + napi_value argv[ARGC_MAX] = { nullptr }; + NAPI_CALL_RETURN_VOID(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr)); + napi_valuetype valueType = napi_undefined; + argc = std::min(argc, maxParamCount); + if (argc > 0) { + napi_typeof(env, argv[argc - 1], &valueType); + if (valueType == napi_function) { + napi_create_reference(env, argv[argc - 1], 1, &context_->callback); + argc = argc - 1; + } + } + NAPI_CALL_RETURN_VOID(env, (*context)(env, argc, argv, self)); + context_->ctx = std::move(context); + napi_create_reference(env, self, 1, &context_->self); +} + +AsyncCall::~AsyncCall() +{ + if (context_ == nullptr) { + return; + } + + DeleteContext(env_, context_); +} + +napi_value AsyncCall::Call(napi_env env, Context::ExecAction exec, const std::string &resourceName) +{ + if (context_ == nullptr) { + SELECTION_HILOGE("context_ is nullptr!"); + return nullptr; + } + if (context_->ctx == nullptr) { + SELECTION_HILOGE("context_->ctx is nullptr!"); + return nullptr; + } + context_->ctx->exec_ = std::move(exec); + napi_value promise = nullptr; + if (context_->callback == nullptr) { + napi_create_promise(env, &context_->defer, &promise); + } else { + napi_get_undefined(env, &promise); + } + napi_async_work work = context_->work; + napi_value resource = nullptr; + std::string name = "SF_" + resourceName; + napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &resource); + napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecute, AsyncCall::OnComplete, context_, &work); + context_->work = work; + context_ = nullptr; + napi_queue_async_work_with_qos(env, work, napi_qos_user_initiated); + return promise; +} + +napi_value AsyncCall::Post(napi_env env, Context::ExecAction exec, std::shared_ptr queue, const char *func) +{ + if (context_ == nullptr || context_->ctx == nullptr || queue == nullptr) { + SELECTION_HILOGE("context is nullptr!"); + return nullptr; + } + context_->ctx->exec_ = std::move(exec); + napi_value promise = nullptr; + if (context_->callback == nullptr) { + napi_create_promise(env, &context_->defer, &promise); + } else { + napi_get_undefined(env, &promise); + } + napi_async_work work = context_->work; + napi_value resource = nullptr; + napi_create_string_utf8(env, func, NAPI_AUTO_LENGTH, &resource); + napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecuteSeq, AsyncCall::OnComplete, context_, &work); + context_->work = work; + context_->queue = queue; + std::unique_lock lock(queue->queuesMutex_); + queue->taskQueue_.emplace(env, work, func); + if (!queue->isRunning) { + auto status = napi_queue_async_work_with_qos(env, work, napi_qos_user_initiated); + queue->isRunning = status == napi_ok; + if (status != napi_ok) { + SELECTION_HILOGE("async work failed.status:%{public}d, func:%{public}s!", status, func); + } + } + context_ = nullptr; + return promise; +} + +napi_value AsyncCall::SyncCall(napi_env env, AsyncCall::Context::ExecAction exec) +{ + if ((context_ == nullptr) || (context_->ctx == nullptr)) { + SELECTION_HILOGE("context_ or context_->ctx is nullptr!"); + return nullptr; + } + context_->ctx->exec_ = std::move(exec); + napi_value promise = nullptr; + if (context_->callback == nullptr) { + napi_create_promise(env, &context_->defer, &promise); + } else { + napi_get_undefined(env, &promise); + } + AsyncCall::OnExecute(env, context_); + AsyncCall::OnComplete(env, context_->ctx->status_, context_); + return promise; +} + +void AsyncCall::OnExecute(napi_env env, void *data) +{ + AsyncContext *context = reinterpret_cast(data); + if (context == nullptr || context->ctx == nullptr) { + SELECTION_HILOGE("context or context->ctx is nullptr!"); + return; + } + context->ctx->Exec(); +} + +void AsyncCall::OnExecuteSeq(napi_env env, void *data) +{ + OnExecute(env, data); + AsyncContext *context = reinterpret_cast(data); + if (context == nullptr || context->queue == nullptr) { + SELECTION_HILOGE("context or context->queue is nullptr!"); + return; + } + auto queue = context->queue; + std::unique_lock lock(queue->queuesMutex_); + if (!queue->taskQueue_.empty()) { + queue->taskQueue_.pop(); + } + queue->isRunning = !queue->taskQueue_.empty() && + napi_queue_async_work_with_qos(queue->taskQueue_.front().env, + queue->taskQueue_.front().work, napi_qos_user_initiated) == napi_ok; +} + +void AsyncCall::OnComplete(napi_env env, napi_status status, void *data) +{ + AsyncContext *context = reinterpret_cast(data); + napi_value output = nullptr; + if (context == nullptr || context->ctx == nullptr) { + SELECTION_HILOGE("context or context->ctx is nullptr!"); + return; + } + napi_status runStatus = (*context->ctx)(env, &output); + napi_value result[ARG_BUTT] = { 0 }; + if (status == napi_ok && runStatus == napi_ok) { + napi_get_undefined(env, &result[ARG_ERROR]); + if (output != nullptr) { + SELECTION_HILOGD("output != nullptr!"); + result[ARG_DATA] = output; + } else { + SELECTION_HILOGD("output is nullptr!"); + napi_get_undefined(env, &result[ARG_DATA]); + } + } else { + SELECTION_HILOGE("failed, [status:%{public}d, runStatus:%{public}d, errorCode:%{public}d, \ + errMessage:%{public}s].", status, runStatus, context->ctx->errorCode_, context->ctx->errMessage_.c_str()); + result[ARG_ERROR] = JsUtils::ToError(env, context->ctx->errorCode_, context->ctx->errMessage_); + napi_get_undefined(env, &result[ARG_DATA]); + } + if (context->defer != nullptr) { + if (status == napi_ok && runStatus == napi_ok) { + napi_resolve_deferred(env, context->defer, result[ARG_DATA]); + } else { + napi_reject_deferred(env, context->defer, result[ARG_ERROR]); + } + } else { + napi_value callback = nullptr; + napi_get_reference_value(env, context->callback, &callback); + napi_value returnValue; + napi_call_function(env, nullptr, callback, ARG_BUTT, result, &returnValue); + } + DeleteContext(env, context); +} + +void AsyncCall::DeleteContext(napi_env env, AsyncContext *context) +{ + if (env != nullptr) { + napi_delete_reference(env, context->callback); + napi_delete_reference(env, context->self); + napi_delete_async_work(env, context->work); + } + delete context; +} + +AsyncCall::InnerTask::InnerTask(napi_env env, napi_async_work work, const char *name) + : env(env), work(work), name(name), startTime(GetTimeStamp()) +{ +} + +AsyncCall::InnerTask::~InnerTask() +{ + auto endTime = GetTimeStamp(); + if (startTime > endTime) { + SELECTION_HILOGE("startTime:%{public}" PRIu64 ", endTime:%{public}" PRIu64, startTime, endTime); + return; + } + if (endTime - startTime > MAX_WAIT_TIME) { + SELECTION_HILOGW("async work timeout! func:%{public}s, startTime:%{public}" PRIu64 ", endTime:%{public}" PRIu64 + ", cost:%{public}" PRIu64 "ms", + name, startTime, endTime, endTime - startTime); + } else { + SELECTION_HILOGD("async work finished! func:%{public}s, startTime:%{public}" PRIu64 ", endTime:%{public}" PRIu64 + ", cost:%{public}" PRIu64 "ms", + name, startTime, endTime, endTime - startTime); + } +} +} // namespace SelectionFwk +} // namespace OHOS diff --git a/frameworks/js/napi/selection_client/async_call.h b/frameworks/js/napi/selection_client/async_call.h new file mode 100644 index 0000000000000000000000000000000000000000..79195ddb9ea46a6123c05201c1a798fdbb0ab32d --- /dev/null +++ b/frameworks/js/napi/selection_client/async_call.h @@ -0,0 +1,147 @@ +/* + * 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 ASYN_CALL_H +#define ASYN_CALL_H + +#include +#include "cpp/mutex.h" +#include "selection_log.h" +#include "selection_js_utils.h" +#include "ffrt.h" +#include "napi/native_api.h" +#include "napi/native_common.h" +#include "napi/native_node_api.h" + + +namespace OHOS { +namespace SelectionFwk { +class AsyncCall final { +public: + class Context { + public: + using InputAction = std::function; + using OutputAction = std::function; + using ExecAction = std::function; + Context(InputAction input, OutputAction output) : input_(std::move(input)), output_(std::move(output)){}; + virtual ~Context(){}; + void SetAction(InputAction input, OutputAction output = nullptr) + { + input_ = input; + output_ = output; + } + + void SetErrorCode(int32_t errorCode) + { + errorCode_ = errorCode; + } + + void SetErrorMessage(const std::string &errMessage) + { + errMessage_ = errMessage; + } + + void SetState(const napi_status &status) + { + status_ = status; + } + + void SetAction(OutputAction output) + { + SetAction(nullptr, std::move(output)); + } + + virtual napi_status operator()(napi_env env, size_t argc, napi_value *argv, napi_value self) + { + if (input_ == nullptr) { + return napi_ok; + } + auto ret = input_(env, argc, argv, self); + input_ = nullptr; + return ret; + } + + virtual napi_status operator()(napi_env env, napi_value *result) + { + if (output_ == nullptr) { + *result = nullptr; + return napi_ok; + } + auto ret = output_(env, result); + output_ = nullptr; + return ret; + } + + virtual void Exec() + { + if (exec_ == nullptr) { + return; + } + exec_(this); + exec_ = nullptr; + }; + + protected: + friend class AsyncCall; + InputAction input_ = nullptr; + OutputAction output_ = nullptr; + ExecAction exec_ = nullptr; + napi_status status_ = napi_generic_failure; + int32_t errorCode_ = 0; + std::string errMessage_; + }; + + struct InnerTask { + InnerTask(napi_env env, napi_async_work work, const char *name); + ~InnerTask(); + napi_env env = nullptr; + napi_async_work work = nullptr; + const char *name = nullptr; + uint64_t startTime = 0; + }; + + struct TaskQueue { + ffrt::mutex queuesMutex_; + std::queue taskQueue_; + bool isRunning = false; + }; + + AsyncCall(napi_env env, napi_callback_info info, std::shared_ptr context, size_t maxParamCount); + ~AsyncCall(); + napi_value Call(napi_env env, Context::ExecAction exec = nullptr, const std::string &resourceName = "AsyncCall"); + napi_value Post(napi_env env, Context::ExecAction exec, std::shared_ptr queue, const char *func); + napi_value SyncCall(napi_env env, Context::ExecAction exec = nullptr); + +private: + enum Arg : int { ARG_ERROR, ARG_DATA, ARG_BUTT }; + static void OnExecute(napi_env env, void *data); + static void OnExecuteSeq(napi_env env, void *data); + static void OnComplete(napi_env env, napi_status status, void *data); + struct AsyncContext { + std::shared_ptr ctx = nullptr; + napi_ref callback = nullptr; + napi_ref self = nullptr; + napi_deferred defer = nullptr; + napi_async_work work = nullptr; + std::shared_ptr queue = nullptr; + }; + static void DeleteContext(napi_env env, AsyncContext *context); + + AsyncContext *context_ = nullptr; + napi_env env_ = nullptr; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // ASYNC_CALL_H \ No newline at end of file diff --git a/frameworks/js/napi/selection_client/js_selection_utils.cpp b/frameworks/js/napi/selection_client/js_selection_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cf0a79ca656696909ad84f041a5c46f9a3664cac --- /dev/null +++ b/frameworks/js/napi/selection_client/js_selection_utils.cpp @@ -0,0 +1,46 @@ +/* + * 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 "js_selection_utils.h" +#include "panel_info.h" + +namespace OHOS { +namespace SelectionFwk { +constexpr int32_t STR_MAX_LENGTH = 4096; +constexpr size_t STR_TAIL_LENGTH = 1; +constexpr size_t ARGC_MAX = 6; +constexpr size_t ARGC_ONE = 1; + +/* napi_value <-> PanelInfo */ +napi_status JsSelectionUtils::GetValue(napi_env env, napi_value in, PanelInfo &out) +{ + SELECTION_HILOGD("napi_value -> PanelInfo "); + napi_value propType = nullptr; + napi_status status = napi_get_named_property(env, in, "panelType", &propType); + CHECK_RETURN((status == napi_ok), "no property panelType ", status); + int32_t panelType = 0; + status = JsUtils::GetValue(env, propType, panelType); + CHECK_RETURN((status == napi_ok), "no value of panelType ", status); + out.panelType = PanelType(panelType); + + bool ret = JsUtil::Object::ReadProperty(env, in, "x", out.x); + ret = ret && JsUtil::Object::ReadProperty(env, in, "y", out.y); + ret = ret && JsUtil::Object::ReadProperty(env, in, "width", out.width); + ret = ret && JsUtil::Object::ReadProperty(env, in, "height", out.height); + + return ret ? napi_ok : napi_generic_failure; +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/js/napi/selection_client/js_selection_utils.h b/frameworks/js/napi/selection_client/js_selection_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..acd0c63df3ce36efd6f64fe4452824ae319c02da --- /dev/null +++ b/frameworks/js/napi/selection_client/js_selection_utils.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 JS_SELECTION_UTILS_H +#define JS_SELECTION_UTILS_H + +#include + +#include "ability.h" +#include "selection_log.h" +#include "selection_panel.h" +#include "util.h" +#include "selection_js_utils.h" +#include "napi/native_api.h" +#include "napi/native_common.h" +#include "napi/native_node_api.h" +#include "string_ex.h" +#include "panel_info.h" + +using Ability = OHOS::AppExecFwk::Ability; +namespace OHOS { +namespace SelectionFwk { + +class JsSelectionUtils { +public: + static napi_status GetValue(napi_env env, napi_value in, PanelInfo &out); + +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // JS_SELECTION_UTILS_H \ No newline at end of file diff --git a/frameworks/js/napi/selection_extension_ability/BUILD.gn b/frameworks/js/napi/selection_extension_ability/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..5bb3207b02525ae35ca6f88d6381ccfc86bceb7d --- /dev/null +++ b/frameworks/js/napi/selection_extension_ability/BUILD.gn @@ -0,0 +1,50 @@ +# 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/ohos.gni") +import("//build/templates/abc/ohos_abc.gni") + +es2abc_gen_abc("gen_selection_extension_ability_abc") { + src_js = rebase_path("selection_extension_ability.js") + dst_file = rebase_path(target_out_dir + "/selection_extension_ability.abc") + in_puts = [ "selection_extension_ability.js" ] + out_puts = [ target_out_dir + "/selection_extension_ability.abc" ] + extra_args = [ "--module" ] +} + +gen_js_obj("selection_extension_ability_js") { + input = "selection_extension_ability.js" + output = target_out_dir + "/selection_extension_ability.o" +} + +gen_js_obj("selection_extension_ability_abc") { + input = get_label_info(":gen_selection_extension_ability_abc", + "target_out_dir") + "/selection_extension_ability.abc" + output = target_out_dir + "/selection_extension_ability_abc.o" + dep = ":gen_selection_extension_ability_abc" +} + +ohos_shared_library("selectionextensionability_napi") { + sources = [ "selection_extension_ability_module.cpp" ] + + deps = [ + ":selection_extension_ability_abc", + ":selection_extension_ability_js", + ] + + external_deps = [ "napi:ace_napi" ] + + relative_install_dir = "module/selectioninput" + subsystem_name = "systemabilitymgr" + part_name = "selectionfwk" +} diff --git a/frameworks/js/napi/selection_extension_ability/selection_extension_ability.js b/frameworks/js/napi/selection_extension_ability/selection_extension_ability.js new file mode 100644 index 0000000000000000000000000000000000000000..4851d702e0d5c92691e92d1a842cdcd542e2a478 --- /dev/null +++ b/frameworks/js/napi/selection_extension_ability/selection_extension_ability.js @@ -0,0 +1,28 @@ +/* + * 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. + */ + +let ExtensionAbility = requireNapi('app.ability.ExtensionAbility'); + +class SelectionExtensionAbility extends ExtensionAbility { + onConnect(want) { + console.log('onConnect, want:' + want.abilityName); + } + + onDisconnect(want) { + console.log('onDisconnect'); + } +} + +export default SelectionExtensionAbility; diff --git a/frameworks/js/napi/selection_extension_ability/selection_extension_ability_module.cpp b/frameworks/js/napi/selection_extension_ability/selection_extension_ability_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1ad91690da0df438dd2121200d64e907deaecf7 --- /dev/null +++ b/frameworks/js/napi/selection_extension_ability/selection_extension_ability_module.cpp @@ -0,0 +1,54 @@ +/* + * 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 "native_engine/native_engine.h" + +extern const char _binary_selection_extension_ability_js_start[]; +extern const char _binary_selection_extension_ability_js_end[]; +extern const char _binary_selection_extension_ability_abc_start[]; +extern const char _binary_selection_extension_ability_abc_end[]; + +static napi_module _module = { + .nm_filename = "libselectionextensionability_napi.so/selection_extension_ability.js", + .nm_modname = "selectionInput.SelectionExtensionAbility", +}; + +extern "C" __attribute__((constructor)) void NAPI_selectionInput_SelectionExtensionAbility_AutoRegister() +{ + napi_module_register(&_module); +} + +extern "C" __attribute__((visibility("default"))) void + NAPI_selectionInput_SelectionExtensionAbility_GetJSCode(const char** buf, int* bufLen) +{ + if (buf != nullptr) { + *buf = _binary_selection_extension_ability_js_start; + } + + if (bufLen != nullptr) { + *bufLen = _binary_selection_extension_ability_js_end - _binary_selection_extension_ability_js_start; + } +} + +extern "C" __attribute__((visibility("default"))) void + NAPI_selectionInput_SelectionExtensionAbility_GetABCCode(const char** buf, int* buflen) +{ + if (buf != nullptr) { + *buf = _binary_selection_extension_ability_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_selection_extension_ability_abc_end - _binary_selection_extension_ability_abc_start; + } +} diff --git a/frameworks/js/napi/selection_extension_context/BUILD.gn b/frameworks/js/napi/selection_extension_context/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..35383a2798a433909f7f2bba5f7833d2e9363a61 --- /dev/null +++ b/frameworks/js/napi/selection_extension_context/BUILD.gn @@ -0,0 +1,61 @@ +# 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/es2abc_config.gni") + +import("//build/ohos.gni") + +es2abc_gen_abc("gen_selection_extension_context_abc") { + src_js = rebase_path("selection_extension_context.js") + dst_file = rebase_path(target_out_dir + "/selection_extension_context.abc") + in_puts = [ "selection_extension_context.js" ] + out_puts = [ target_out_dir + "/selection_extension_context.abc" ] + extra_args = [ "--module" ] +} + +gen_js_obj("selection_extension_context_js") { + input = "selection_extension_context.js" + output = target_out_dir + "/selection_extension_context.o" +} + +gen_js_obj("selection_extension_context_abc") { + input = + get_label_info(":gen_selection_extension_context_abc", + "target_out_dir") + "/selection_extension_context.abc" + output = target_out_dir + "/selection_extension_context_abc.o" + dep = ":gen_selection_extension_context_abc" +} + +ohos_shared_library("selectionextensioncontext_napi") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + integer_overflow = true + ubsan = true + } + sources = [ "selection_extension_context_module.cpp" ] + + deps = [ + ":selection_extension_context_abc", + ":selection_extension_context_js", + ] + + external_deps = [ "napi:ace_napi" ] + + relative_install_dir = "module/selectioninput" + subsystem_name = "systemabilitymgr" + part_name = "selectionfwk" +} diff --git a/frameworks/js/napi/selection_extension_context/selection_extension_context.js b/frameworks/js/napi/selection_extension_context/selection_extension_context.js new file mode 100644 index 0000000000000000000000000000000000000000..82e0042d041aa705db49412c8bdca316a8f9ef78 --- /dev/null +++ b/frameworks/js/napi/selection_extension_context/selection_extension_context.js @@ -0,0 +1,29 @@ +/* + * 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. + */ + +const ExtensionContext = requireNapi('application.ExtensionContext'); + +class SelectionExtensionContext extends ExtensionContext { + constructor(obj) { + super(obj); + this.extensionAbilityInfo = obj.extensionAbilityInfo; + } + + startAbility(want) { + return this.__context_impl__.startAbility(want); + } +} + +export default SelectionExtensionContext; \ No newline at end of file diff --git a/frameworks/js/napi/selection_extension_context/selection_extension_context_module.cpp b/frameworks/js/napi/selection_extension_context/selection_extension_context_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f2cd8bb4d3fc04d8850f8009b388b4c88ea5c25 --- /dev/null +++ b/frameworks/js/napi/selection_extension_context/selection_extension_context_module.cpp @@ -0,0 +1,55 @@ +/* + * 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 "native_engine/native_engine.h" + +extern const char _binary_selection_extension_context_js_start[]; +extern const char _binary_selection_extension_context_js_end[]; +extern const char _binary_selection_extension_context_abc_start[]; +extern const char _binary_selection_extension_context_abc_end[]; + +static napi_module g_ExtensionContextModule = { + .nm_version = 0, + .nm_filename = "libselectionextensioncontext_napi.so/selection_extension_context.js", + .nm_modname = "selectionInput.SelectionExtensionContext", +}; + +extern "C" __attribute__((constructor)) void NAPI_selectionInput_SelectionExtensionContext_AutoRegister() +{ + napi_module_register(&g_ExtensionContextModule); +} + +extern "C" __attribute__((visibility("default"))) void NAPI_selectionInput_SelectionExtensionContext_GetJSCode( + const char **buf, int *bufLen) +{ + if (buf != nullptr) { + *buf = _binary_selection_extension_context_js_start; + } + if (bufLen != nullptr) { + *bufLen = _binary_selection_extension_context_js_end - _binary_selection_extension_context_js_start; + } +} + +// ability_context JS register +extern "C" __attribute__((visibility("default"))) void NAPI_selectionInput_SelectionExtensionContext_GetABCCode( + const char **buf, int *buflen) +{ + if (buf != nullptr) { + *buf = _binary_selection_extension_context_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_selection_extension_context_abc_end - _binary_selection_extension_context_abc_start; + } +} diff --git a/frameworks/js/napi/selection_panel/BUILD.gn b/frameworks/js/napi/selection_panel/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..debdfe511a22a9f22985f39d8d69b14de08f127c --- /dev/null +++ b/frameworks/js/napi/selection_panel/BUILD.gn @@ -0,0 +1,59 @@ +# 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/ohos.gni") +import("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +config("selection_panel_config") { + visibility = [ ":*" ] + include_dirs = [ + "./", + "${selection_fwk_root_path}/utils/include", + "${selection_fwk_root_path}/common", + "${selection_fwk_root_path}/frameworks/native/selection_ability/include", + ] +} + +ohos_shared_library("selectionpanel_napi") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + integer_overflow = true + ubsan = true + } + sources = [ + "selection_panel_module.cpp", + "js_selection_panel.cpp", + ] + + configs = [ ":selection_panel_config" ] + + deps = [ + "${selection_fwk_root_path}/common:selection_common", + "${selection_fwk_root_path}/frameworks/native/selection_ability:selection_ability", + ] + + external_deps = [ + "ability_runtime:runtime", + "c_utils:utils", + "hilog:libhilog", + "napi:ace_napi", + ] + + relative_install_dir = "module/selectioninput" + subsystem_name = "systemabilitymgr" + part_name = "selectionfwk" +} diff --git a/frameworks/js/napi/selection_panel/js_selection_panel.cpp b/frameworks/js/napi/selection_panel/js_selection_panel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45f592e0aafa535374e5680e355de2bec04c14e3 --- /dev/null +++ b/frameworks/js/napi/selection_panel/js_selection_panel.cpp @@ -0,0 +1,49 @@ +/* + * 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 "js_selection_panel.h" + +#include "js_runtime_utils.h" +#include "util.h" +#include "panel_info.h" +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { +napi_value JsSelectionPanel::Init(napi_env env, napi_value exports) +{ + napi_set_named_property(env, exports, "PanelType", GetJsPanelTypeProperty(env)); + return exports; +} + +napi_value JsSelectionPanel::GetJsPanelTypeProperty(napi_env env) +{ + napi_value obj = nullptr; + NAPI_CALL(env, napi_create_object(env, &obj)); + + auto ret = JsUtil::Object::WriteProperty(env, obj, "MENU_PANEL", static_cast(PanelType::MENU_PANEL)); + if (!ret) { + SELECTION_HILOGE("Failed to init module selectionInput.SelectionPanel.PanelType as MENU_PANEL"); + return nullptr; + } + ret = JsUtil::Object::WriteProperty(env, obj, "MAIN_PANEL", static_cast(PanelType::MAIN_PANEL)); + if (!ret) { + SELECTION_HILOGE("Failed to init module selectionInput.SelectionPanel.PanelType as MAIN_PANEL"); + return nullptr; + } + return obj; +} +} // namespace MiscServices +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/js/napi/selection_panel/js_selection_panel.h b/frameworks/js/napi/selection_panel/js_selection_panel.h new file mode 100644 index 0000000000000000000000000000000000000000..5e2f8216d64334812085a23f3e2233015788cf17 --- /dev/null +++ b/frameworks/js/napi/selection_panel/js_selection_panel.h @@ -0,0 +1,34 @@ +/* + * 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 INTERFACE_JS_SELECTION_PANEL +#define INTERFACE_JS_SELECTION_PANEL + +#include "napi/native_api.h" + +namespace OHOS { +namespace SelectionFwk { +class JsSelectionPanel { +public: + JsSelectionPanel() = default; + ~JsSelectionPanel() = default; + static napi_value Init(napi_env env, napi_value exports); + +private: + static napi_value GetJsPanelTypeProperty(napi_env env); +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // INTERFACE_JS_SELECTION_PANEL \ No newline at end of file diff --git a/frameworks/js/napi/selection_panel/selection_panel_module.cpp b/frameworks/js/napi/selection_panel/selection_panel_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5be47cd3805e12dc5159b488d5718a5a4892eda3 --- /dev/null +++ b/frameworks/js/napi/selection_panel/selection_panel_module.cpp @@ -0,0 +1,47 @@ +/* + * 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 "js_selection_panel.h" +#include "napi/native_api.h" + +EXTERN_C_START +/* + * function for module exports + */ +static napi_value Init(napi_env env, napi_value exports) +{ + OHOS::SelectionFwk::JsSelectionPanel::Init(env, exports); + return exports; +} +EXTERN_C_END +/* + * module define + */ +static napi_module _module = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "selectionInput.SelectionPanel", + .nm_priv = ((void *)0), + .reserved = { 0 } +}; +/* + * module register + */ +extern "C" __attribute__((constructor)) void NAPI_selectionInput_SelectionPanel_AutoRegister() +{ + napi_module_register(&_module); +} \ No newline at end of file diff --git a/frameworks/native/selection_ability/BUILD.gn b/frameworks/native/selection_ability/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..856965ffbab1a566b355018a8cc5b421710ee046 --- /dev/null +++ b/frameworks/native/selection_ability/BUILD.gn @@ -0,0 +1,81 @@ +# 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/ohos.gni") +import("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +config("selection_listener_config") { + include_dirs = [ + "include", + "../../../utils/include", + "${target_gen_dir}", + "${selection_fwk_root_path}/common", + "${selection_fwk_root_path}/frameworks/common", + ] +} + +ohos_shared_library("selection_ability") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + integer_overflow = true + ubsan = true + } + configs = [ ":selection_listener_config", ] + + sources = [ + "src/selection_ability.cpp", + "src/selection_listener_impl.cpp", + "src/selection_panel.cpp", + "src/task_manager.cpp", + "src/tasks/task.cpp", + "src/selection_app_validator.cpp", + "src/selection_panel_manager.cpp", + ] + deps = [ + "${selection_fwk_root_path}/common:selection_common", + "${selection_fwk_root_path}/interfaces/idl:selection_listener_interface", + "${selection_fwk_root_path}/interfaces/idl:selection_listener_stub", + ] + + external_deps = [ + "c_utils:utils", + "ipc:ipc_single", + "hilog:libhilog", + "ability_base:configuration", + "ability_base:want", + "ability_runtime:ability_context_native", + "ability_runtime:app_context", + "bundle_framework:appexecfwk_base", + "config_policy:configpolicy_util", + "eventhandler:libeventhandler", + "graphic_2d:librender_service_base", + "graphic_2d:librender_service_client", + "graphic_2d:window_animation", + "input:libmmi-client", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + "window_manager:libdm", + "window_manager:libwsutils", + "ability_runtime:ability_manager", + ] + + public_external_deps = [ "window_manager:libwm" ] + + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} \ No newline at end of file diff --git a/frameworks/native/selection_ability/ISelectionListener.idl b/frameworks/native/selection_ability/ISelectionListener.idl new file mode 100644 index 0000000000000000000000000000000000000000..e8d68c09acd56a9872f60245ff9c1e898d2f9cbb --- /dev/null +++ b/frameworks/native/selection_ability/ISelectionListener.idl @@ -0,0 +1,21 @@ +/* + * 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. + */ + +sequenceable selection_data_inner..OHOS.SelectionFwk.SelectionInfoData; + +interface OHOS.SelectionFwk.ISelectionListener { + void OnSelectionChange([in] SelectionInfoData selectionInfoData); + void FocusChange([in] unsigned int windowID, [in] unsigned int windowType); +} diff --git a/frameworks/native/selection_ability/include/actions/action.h b/frameworks/native/selection_ability/include/actions/action.h new file mode 100644 index 0000000000000000000000000000000000000000..65d2813d4f79e8eb6767722531835e450c6760ad --- /dev/null +++ b/frameworks/native/selection_ability/include/actions/action.h @@ -0,0 +1,69 @@ +/* + * 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 FRAMEWORKS_SELECTION_ABILITY_INCLUDE_ACTIONS_ACTION_H +#define FRAMEWORKS_SELECTION_ABILITY_INCLUDE_ACTIONS_ACTION_H + +#include +#include + +namespace OHOS { +namespace SelectionFwk { + +enum RunningState : uint32_t { + RUNNING_STATE_IDLE = 0, + RUNNING_STATE_RUNNING, + RUNNING_STATE_PAUSED, + RUNNING_STATE_COMPLETED, + RUNNING_STATE_ERROR, +}; + +class Action { +public: + Action() = default; + Action(std::function func) : func_(func) { } + virtual ~Action() = default; + + virtual RunningState Execute() + { + if (state_ != RUNNING_STATE_IDLE) { + return RUNNING_STATE_ERROR; + } + + state_ = RUNNING_STATE_RUNNING; + if (func_) { + func_(); + } + state_ = RUNNING_STATE_COMPLETED; + return state_; + } + + virtual RunningState Resume(uint64_t resumeId) + { + return state_; + } + + RunningState GetState() const + { + return state_; + } + +protected: + RunningState state_ { RUNNING_STATE_IDLE }; + std::function func_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // FRAMEWORKS_SELECTION_ABILITY_INCLUDE_ACTIONS_ACTION_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/actions/action_wait.h b/frameworks/native/selection_ability/include/actions/action_wait.h new file mode 100644 index 0000000000000000000000000000000000000000..5ea82714874c37c1496caf6a216e1fab9b1498e4 --- /dev/null +++ b/frameworks/native/selection_ability/include/actions/action_wait.h @@ -0,0 +1,88 @@ +/* + * 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 FRAMEWORKS_SELECTION_ABILITY_INCLUDE_ACTIONS_ACTION_WAIT_H +#define FRAMEWORKS_SELECTION_ABILITY_INCLUDE_ACTIONS_ACTION_WAIT_H + +#include +#include + +#include "action.h" +#include "task_manager.h" +#include "tasks/task_inner.h" + +namespace OHOS { +namespace SelectionFwk { + +class ActionWait : public Action { +public: + using callback_t = std::function; + + ActionWait(uint64_t completeId, uint32_t timeoutMs) + : timeoutMs_(timeoutMs), completeId_(completeId), timeoutId_(Task::GetNextSeqId()) + { + } + + ActionWait(uint64_t completeId, uint32_t timeoutMs, callback_t onComplete, callback_t onTimeout) + : timeoutMs_(timeoutMs), completeId_(completeId), timeoutId_(Task::GetNextSeqId()), onComplete_(onComplete), + onTimeout_(onTimeout) + { + } + + ~ActionWait() = default; + + RunningState Execute() override + { + state_ = RUNNING_STATE_PAUSED; + // trigger timeout with delay + auto task = std::make_shared(timeoutId_); + TaskManager::GetInstance().PostTask(task, timeoutMs_); + return state_; + } + + RunningState Resume(uint64_t seqId) override + { + if (state_ != RUNNING_STATE_PAUSED) { + return RUNNING_STATE_ERROR; + } + + if (seqId == completeId_) { + if (onComplete_) { + onComplete_(); + } + state_ = RUNNING_STATE_COMPLETED; + return state_; + } + if (seqId == timeoutId_) { + if (onTimeout_) { + onTimeout_(); + } + state_ = RUNNING_STATE_COMPLETED; + return state_; + } + + return state_; + } + +private: + const uint32_t timeoutMs_; + const uint64_t completeId_; + const uint64_t timeoutId_; + std::function onComplete_; + std::function onTimeout_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // FRAMEWORKS_SELECTION_ABILITY_INCLUDE_ACTIONS_ACTION_WAIT_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/panel_common.h b/frameworks/native/selection_ability/include/panel_common.h new file mode 100644 index 0000000000000000000000000000000000000000..5bd533f86973bf43659aebccebe42dbc042eb65f --- /dev/null +++ b/frameworks/native/selection_ability/include/panel_common.h @@ -0,0 +1,96 @@ +/* + * 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 SELECTIONFWK_PANEL_COMMON_H +#define SELECTIONFWK_PANEL_COMMON_H + +#include + +#include "wm_common.h" + +namespace OHOS { +namespace SelectionFwk { +struct WindowSize { + uint32_t width = 0; + uint32_t height = 0; +}; + +struct LayoutParams { + Rosen::Rect landscapeRect{ 0, 0, 0, 0 }; + Rosen::Rect portraitRect{ 0, 0, 0, 0 }; +}; + +struct HotArea { + std::vector keyboardHotArea; + std::vector panelHotArea; + static std::string ToString(const std::vector &areas) + { + std::string areasStr = "["; + for (const auto& area : areas) { + areasStr.append(area.ToString()); + } + areasStr.append("]"); + return areasStr; + } +}; + +struct HotAreas { + HotArea landscape; + HotArea portrait; + bool isSet{ false }; +}; + +struct EnhancedLayoutParam { + Rosen::Rect rect{ 0, 0, 0, 0 }; + int32_t avoidY{ 0 }; + uint32_t avoidHeight{ 0 }; + inline std::string ToString() const + { + std::stringstream ss; + ss << "rect" << rect.ToString() << " avoidY " << avoidY << " avoidHeight " << avoidHeight; + return ss.str(); + } +}; + +struct EnhancedLayoutParams { + bool isFullScreen{ false }; + EnhancedLayoutParam portrait; + EnhancedLayoutParam landscape; +}; + +struct DisplaySize { + WindowSize portrait; + WindowSize landscape; +}; + +struct PanelAdjustInfo { + int32_t top{ 0 }; + int32_t left{ 0 }; + int32_t right{ 0 }; + int32_t bottom{ 0 }; + bool operator==(const PanelAdjustInfo &panelAdjust) const + { + return (top == panelAdjust.top && left == panelAdjust.left && right == panelAdjust.right + && bottom == panelAdjust.bottom); + } +}; + +struct FullPanelAdjustInfo { + PanelAdjustInfo portrait; + PanelAdjustInfo landscape; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif //SELECTIONFWK_PANEL_COMMON_H diff --git a/frameworks/native/selection_ability/include/panel_info.h b/frameworks/native/selection_ability/include/panel_info.h new file mode 100644 index 0000000000000000000000000000000000000000..5bebfb870b48d055cd6ad6e0e20e82d018e5d9e6 --- /dev/null +++ b/frameworks/native/selection_ability/include/panel_info.h @@ -0,0 +1,46 @@ +/* + * 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 SELECTIONFWK_PANEL_INFO_H +#define SELECTIONFWK_PANEL_INFO_H + +#include "parcel.h" + +namespace OHOS { +namespace SelectionFwk { +enum class PanelType: uint32_t { + MENU_PANEL = 1, + MAIN_PANEL = 2, +}; + +struct PanelInfo { + PanelType panelType{}; + int32_t x = 0; + int32_t y = 0; + int32_t width = 0; + int32_t height = 0; +}; + +enum class ImmersiveMode : int32_t { + NONE_IMMERSIVE = 0, + IMMERSIVE = 1, + LIGHT_IMMERSIVE = 2, + DARK_IMMERSIVE = 3, + END, +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // SELECTIONFWK_PANEL_INFO_H diff --git a/frameworks/native/selection_ability/include/panel_status_listener.h b/frameworks/native/selection_ability/include/panel_status_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..8080d4f890dca0045984475a46ca21d1741e83f1 --- /dev/null +++ b/frameworks/native/selection_ability/include/panel_status_listener.h @@ -0,0 +1,32 @@ +/* + * 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 SELECTION_PANEL_STATUS_LISTENER_H +#define SELECTION_PANEL_STATUS_LISTENER_H +#include + +#include "panel_common.h" + +namespace OHOS { +namespace SelectionFwk { +class PanelStatusListener { +public: + virtual ~PanelStatusListener() {}; + virtual void OnPanelStatus(uint32_t windowId, const std::string& status) = 0; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // SELECTION_PANEL_STATUS_LISTENER_H diff --git a/frameworks/native/selection_ability/include/selection_ability.h b/frameworks/native/selection_ability/include/selection_ability.h new file mode 100644 index 0000000000000000000000000000000000000000..a361419432773e6d4c530582de42a3894585ac07 --- /dev/null +++ b/frameworks/native/selection_ability/include/selection_ability.h @@ -0,0 +1,51 @@ +/* + * 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 FRAMEWORKS_SELECTION_ABILITY_INCLUDE_SELECTION_ABILITY_H +#define FRAMEWORKS_SELECTION_ABILITY_INCLUDE_SELECTION_ABILITY_H + +#include +#include +#include +#include "refbase.h" +#include "context.h" +#include "concurrent_map.h" +#include "selection_panel.h" +#include "panel_info.h" + +namespace OHOS { +namespace SelectionFwk{ +class SelectionAbility : public RefBase { +public: + SelectionAbility(); + ~SelectionAbility(); + static sptr GetInstance(); + int32_t CreatePanel(const std::shared_ptr &context, const PanelInfo &panelInfo, + std::shared_ptr &selectionPanel); + int32_t DestroyPanel(const std::shared_ptr &selectionPanel); + int32_t ShowPanel(const std::shared_ptr &selectionPanel); + int32_t HidePanel(const std::shared_ptr &selectionPanel); + +private: + static std::mutex instanceLock_; + static sptr instance_; + + ConcurrentMap> panels_ {}; + std::atomic_bool isShowAfterCreate_ { false }; + +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // FRAMEWORKS_SELECTION_ABILITY_INCLUDE_SELECTION_ABILITY_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/selection_app_validator.h b/frameworks/native/selection_ability/include/selection_app_validator.h new file mode 100644 index 0000000000000000000000000000000000000000..1a1af257b187a56c98b53056d5528612dfa68553 --- /dev/null +++ b/frameworks/native/selection_ability/include/selection_app_validator.h @@ -0,0 +1,42 @@ +/* + * 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 SELECTION_NATIVE_APP_VALIDATOR_H +#define SELECTION_NATIVE_APP_VALIDATOR_H + +#include +#include +#include + +namespace OHOS::SelectionFwk { +class SelectionAppValidator { +public: + static SelectionAppValidator& GetInstance(); + void SetValid(); + bool Validate() const; + +private: + SelectionAppValidator() = default; + SelectionAppValidator(const SelectionAppValidator&) = delete; + SelectionAppValidator(SelectionAppValidator&&) = delete; + SelectionAppValidator& operator= (const SelectionAppValidator&) = delete; + SelectionAppValidator& operator= (SelectionAppValidator&&) = delete; + +private: + bool isValid_ = false; +}; + +} // namespace OHOS::SelectionFwk +#endif // SELECTION_NATIVE_APP_VALIDATOR_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/selection_attribute.h b/frameworks/native/selection_ability/include/selection_attribute.h new file mode 100644 index 0000000000000000000000000000000000000000..911b8f828af0ae20fb75086cb54415212bbc70fe --- /dev/null +++ b/frameworks/native/selection_ability/include/selection_attribute.h @@ -0,0 +1,148 @@ +/* + * 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 SERVICES_INCLUDE_INPUT_ATTRIBUTE_H +#define SERVICES_INCLUDE_INPUT_ATTRIBUTE_H + +#include +#include + +#include "parcel.h" + +namespace OHOS { +namespace SelectionFwk { + +struct InputAttribute { + static const int32_t PATTERN_TEXT = 0x00000001; + static const int32_t PATTERN_PASSWORD = 0x00000007; + static const int32_t PATTERN_PASSWORD_NUMBER = 0x00000008; + static const int32_t PATTERN_PASSWORD_SCREEN_LOCK = 0x00000009; + static const int32_t PATTERN_NEWPASSWORD = 0x0000000b; + int32_t inputPattern = 0; + int32_t enterKeyType = 0; + int32_t inputOption = 0; + bool isTextPreviewSupported { false }; + std::string bundleName { "" }; + int32_t immersiveMode = 0; + uint32_t windowId = 0; // for transfer + uint64_t callingDisplayId = 0; + + bool GetSecurityFlag() const + { + return inputPattern == PATTERN_PASSWORD || inputPattern == PATTERN_PASSWORD_SCREEN_LOCK || + inputPattern == PATTERN_PASSWORD_NUMBER || inputPattern == PATTERN_NEWPASSWORD; + } + + bool operator==(const InputAttribute &info) const + { + return inputPattern == info.inputPattern && enterKeyType == info.enterKeyType && + inputOption == info.inputOption && isTextPreviewSupported == info.isTextPreviewSupported; + } + + inline std::string ToString() const + { + std::stringstream ss; + ss << "[" << "inputPattern:" << inputPattern + << ", enterKeyType:" << enterKeyType << ", inputOption:" << inputOption + << ", isTextPreviewSupported:" << isTextPreviewSupported << ", bundleName:" << bundleName + << ", immersiveMode:" << immersiveMode << ", windowId:" << windowId + << ", callingDisplayId:" << callingDisplayId << "]"; + return ss.str(); + } +}; + +struct InputAttributeInner : public Parcelable { + static const int32_t PATTERN_TEXT = 0x00000001; + static const int32_t PATTERN_PASSWORD = 0x00000007; + static const int32_t PATTERN_PASSWORD_NUMBER = 0x00000008; + static const int32_t PATTERN_PASSWORD_SCREEN_LOCK = 0x00000009; + static const int32_t PATTERN_NEWPASSWORD = 0x0000000b; + int32_t inputPattern = 0; + int32_t enterKeyType = 0; + int32_t inputOption = 0; + bool isTextPreviewSupported { false }; + std::string bundleName { "" }; + int32_t immersiveMode = 0; + uint32_t windowId = 0; // for transfer + uint64_t callingDisplayId = 0; + + bool ReadFromParcel(Parcel &in) + { + inputPattern = in.ReadInt32(); + enterKeyType = in.ReadInt32(); + inputOption = in.ReadInt32(); + isTextPreviewSupported = in.ReadBool(); + bundleName = in.ReadString(); + immersiveMode = in.ReadInt32(); + windowId = in.ReadUint32(); + callingDisplayId = in.ReadUint64(); + return true; + } + + bool Marshalling(Parcel &out) const + { + if (!out.WriteInt32(inputPattern)) { + return false; + } + if (!out.WriteInt32(enterKeyType)) { + return false; + } + if (!out.WriteInt32(inputOption)) { + return false; + } + if (!out.WriteBool(isTextPreviewSupported)) { + return false; + } + if (!out.WriteString(bundleName)) { + return false; + } + if (!out.WriteInt32(immersiveMode)) { + return false; + } + if (!out.WriteUint32(windowId)) { + return false; + } + if (!out.WriteUint64(callingDisplayId)) { + return false; + } + return true; + } + + static InputAttributeInner *Unmarshalling(Parcel &in) + { + InputAttributeInner *data = new (std::nothrow) InputAttributeInner(); + if (data && !data->ReadFromParcel(in)) { + delete data; + data = nullptr; + } + return data; + } + + bool operator==(const InputAttribute &info) const + { + return inputPattern == info.inputPattern && enterKeyType == info.enterKeyType && + inputOption == info.inputOption && isTextPreviewSupported == info.isTextPreviewSupported; + } + + bool GetSecurityFlag() const + { + return inputPattern == PATTERN_PASSWORD || inputPattern == PATTERN_PASSWORD_SCREEN_LOCK || + PATTERN_PASSWORD_NUMBER == inputPattern || PATTERN_NEWPASSWORD == inputPattern; + } +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // SERVICES_INCLUDE_INPUT_ATTRIBUTE_H diff --git a/frameworks/native/selection_ability/include/selection_listener_impl.h b/frameworks/native/selection_ability/include/selection_listener_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..224f09887ace87c8f42a9a4f5bfb78e2897764d2 --- /dev/null +++ b/frameworks/native/selection_ability/include/selection_listener_impl.h @@ -0,0 +1,36 @@ +/* + * 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 SELECTION_LISTENER_IMPL_H +#define SELECTION_LISTENER_IMPL_H +#include "refbase.h" +#include "selection_listener_stub.h" +#include "selection_interface.h" + +namespace OHOS { +namespace SelectionFwk { +class SelectionListenerImpl : public SelectionListenerStub { +public: + SelectionListenerImpl(std::shared_ptr selectionI) : selectionI_(selectionI) {} + ~SelectionListenerImpl() override = default; + ErrCode OnSelectionChange(const SelectionInfoData& SelectionInfoData) override; + ErrCode FocusChange(const SelectionFocusChangeInfo& focusChangeInfo) override; + +private: + std::shared_ptr selectionI_; +}; +} +} +#endif // SELECTION_LISTENER_IMPL_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/selection_panel.h b/frameworks/native/selection_ability/include/selection_panel.h new file mode 100644 index 0000000000000000000000000000000000000000..871659a7806ec062e8d77e8c862c6e17cd6bdb83 --- /dev/null +++ b/frameworks/native/selection_ability/include/selection_panel.h @@ -0,0 +1,98 @@ +/* + * 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 SELECTION_PANEL_H +#define SELECTION_PANEL_H + +#include +#include +#include +#include +#include +#include + +#include "panel_common.h" +#include "panel_info.h" +#include "refbase.h" +#include "context.h" +#include "wm_common.h" +#include "window.h" +#include "ui/rs_surface_node.h" +#include "panel_status_listener.h" + +namespace OHOS { +namespace SelectionFwk { +enum class SelectionWindowStatus : uint32_t { + HIDDEN, + DESTROYED, + NONE +}; + +class SelectionPanel { +public: + static constexpr uint32_t INVALID_WINDOW_ID = 0; + SelectionPanel() = default; + ~SelectionPanel(); + int32_t CreatePanel(const std::shared_ptr &context, const PanelInfo &panelInfo); + int32_t DestroyPanel(); + int32_t SetUiContent(const std::string &contentInfo, napi_env env); + int32_t ShowPanel(); + int32_t HidePanel(); + int32_t StartMoving(); + int32_t MoveTo(int32_t x, int32_t y); + PanelType GetPanelType(); + bool IsShowing(); + bool IsHidden(); + bool IsDestroyed() const; + bool SetPanelStatusListener(std::shared_ptr statusListener, const std::string &type); + void ClearPanelListener(const std::string &type); + uint32_t GetWindowId(); + + uint32_t windowId_ = INVALID_WINDOW_ID; + +private: + inline static const std::unordered_map panelStatusMap_ { + { SelectionWindowStatus::HIDDEN, "hidden" }, + { SelectionWindowStatus::DESTROYED, "destroyed" } + }; + std::string GeneratePanelName(); + int32_t SetPanelProperties(); + static uint32_t GenerateSequenceId(); + void PanelStatusChange(const SelectionWindowStatus &status); + bool MarkListener(const std::string &type, bool isRegister); + bool IsPanelListenerClearable(); + + PanelType panelType_ = PanelType::MENU_PANEL; + int32_t x_ = 0; + int32_t y_ = 0; + int32_t width_ = 0; + int32_t height_ = 0; + + static std::mutex windowMutex_; + sptr window_; + sptr winOption_ = nullptr; + bool isScbEnable_ { false }; + Rosen::KeyboardLayoutParams keyboardLayoutParams_; + static std::atomic sequenceId_; + uint32_t invalidGravityPercent = 0; + std::atomic isWaitSetUiContent_ { true }; + std::shared_ptr panelStatusListener_ = nullptr; + bool destroyedRegistered_ = false; + bool hiddenRegistered_ = false; + SelectionWindowStatus windowStatus_ = SelectionWindowStatus::NONE; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // SELECTION_PANEL_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/selection_panel_manager.h b/frameworks/native/selection_ability/include/selection_panel_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..9e68b651c6bba5fc2e0e1b1c2c39fa9827c5c841 --- /dev/null +++ b/frameworks/native/selection_ability/include/selection_panel_manager.h @@ -0,0 +1,51 @@ +/* + * 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 SELECTION_PANEL_MANAGER_H +#define SELECTION_PANEL_MANAGER_H +#include +#include +#include +#include +#include + +#include "selection_panel.h" + +namespace OHOS { +namespace SelectionFwk { +class SelectionPanelManager { +public: + static SelectionPanelManager& GetInstance(); + + ~SelectionPanelManager() = default; + void AddSelectionPanel(uint32_t id, std::shared_ptr &obj); + std::shared_ptr GetSelectionPanel(uint32_t id) const; + bool FindWindowID(uint32_t id) const; + void Dispose(); + +private: + SelectionPanelManager() = default; + + SelectionPanelManager(const SelectionPanelManager& other) = delete; + SelectionPanelManager& operator=(const SelectionPanelManager& other) = delete; + +private: + std::unordered_map> storage_; + mutable std::mutex mutex_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // SELECTION_PANEL_MANAGER_H diff --git a/frameworks/native/selection_ability/include/selection_panel_manger.h b/frameworks/native/selection_ability/include/selection_panel_manger.h new file mode 100644 index 0000000000000000000000000000000000000000..77b9c07f4b1c16a7f567e3470003a9fbcf6c2694 --- /dev/null +++ b/frameworks/native/selection_ability/include/selection_panel_manger.h @@ -0,0 +1,68 @@ +/* + * 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 SELECTION_PANEL_MANGER_H +#define SELECTION_PANEL_MANGER_H +#include +#include +#include +#include +#include + +#include "selection_panel.h" + +namespace OHOS { +namespace SelectionFwk { +class SelectionPanelManger { +public: + static SelectionPanelManger& GetInstance() { + static SelectionPanelManger instance; + return instance; + } + + void AddSelectionPanel(int32_t id, std::shared_ptr &obj) { + std::lock_guard lock(mutex_); + storage_[id] = obj; + } + + std::shared_ptr GetSelectionPanel(int32_t id) const { + std::lock_guard lock(mutex_); + auto it = storage_.find(id); + return (it != storage_.end()) ? it->second : nullptr; + } + + void RemoveSelectionPanel(int32_t id) { + std::lock_guard lock(mutex_); + storage_.erase(id); + } + + bool FindWindowID(uint32_t id) const { + std::lock_guard lock(mutex_); + return storage_.find(id) != storage_.end(); + } + +private: + SelectionPanelManger() = default; + ~SelectionPanelManger() = default; + std::unordered_map> storage_; + mutable std::mutex mutex_; + +}; +} // namespace SelectionFwk +} // namespace OHOS + + +#endif // SELECTION_PANEL_MANGER_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/selection_window_info.h b/frameworks/native/selection_ability/include/selection_window_info.h new file mode 100644 index 0000000000000000000000000000000000000000..5949aba571f28a03e7f4187209075b4bb847cc60 --- /dev/null +++ b/frameworks/native/selection_ability/include/selection_window_info.h @@ -0,0 +1,123 @@ +/* + * 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 FRAMEWORKS_SELECTION_INCLUDE_SELECTION_WINDOW_INFO_H +#define FRAMEWORKS_SELECTION_INCLUDE_SELECTION_WINDOW_INFO_H + +#include +#include +#include "parcel.h" + +#include "panel_info.h" +namespace OHOS { +namespace SelectionFwk { +enum class SelectionWindowStatus : uint32_t { + HIDDEN, + DESTROYED, + NONE +}; + +struct SelectionWindowInfo : public Parcelable { + std::string name; // the name of inputWindow + int32_t left { 0 }; // the abscissa of the upper-left vertex of inputWindow + int32_t top { 0 }; // the ordinate of the upper-left vertex of inputWindow + uint32_t width { 0 }; // the width of inputWindow + uint32_t height { 0 }; // the height of inputWindow + + bool ReadFromParcel(Parcel &in) + { + name = in.ReadString(); + left = in.ReadInt32(); + top = in.ReadInt32(); + width = in.ReadUint32(); + height = in.ReadUint32(); + return true; + } + + bool Marshalling(Parcel &out) const + { + if (!out.WriteString(name)) { + return false; + } + if (!out.WriteInt32(left)) { + return false; + } + if (!out.WriteInt32(top)) { + return false; + } + if (!out.WriteUint32(width)) { + return false; + } + if (!out.WriteUint32(height)) { + return false; + } + return true; + } + + static SelectionWindowInfo *Unmarshalling(Parcel &in) + { + SelectionWindowInfo *data = new (std::nothrow) SelectionWindowInfo(); + if (data && !data->ReadFromParcel(in)) { + delete data; + data = nullptr; + } + return data; + } +}; + +struct ImeWindowInfo : public Parcelable { + PanelInfo panelInfo; + SelectionWindowInfo windowInfo; + + bool ReadFromParcel(Parcel &in) + { + std::unique_ptr pInfo(in.ReadParcelable()); + if (pInfo == nullptr) { + return false; + } + panelInfo = *pInfo; + + std::unique_ptr wInfo(in.ReadParcelable()); + if (wInfo == nullptr) { + return false; + } + windowInfo = *wInfo; + return true; + } + + bool Marshalling(Parcel &out) const + { + if (!out.WriteParcelable(&panelInfo)) { + return false; + } + if (!out.WriteParcelable(&windowInfo)) { + return false; + } + return true; + } + static ImeWindowInfo *Unmarshalling(Parcel &in) + { + ImeWindowInfo *data = new (std::nothrow) ImeWindowInfo(); + if (data && !data->ReadFromParcel(in)) { + delete data; + data = nullptr; + } + return data; + } +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // FRAMEWORKS_SELECTION_INCLUDE_SELECTION_WINDOW_INFO_H diff --git a/frameworks/native/selection_ability/include/task_manager.h b/frameworks/native/selection_ability/include/task_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..bf73c20aea5fb72e765548a67fa97f762803d73c --- /dev/null +++ b/frameworks/native/selection_ability/include/task_manager.h @@ -0,0 +1,90 @@ +/* + * 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 FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASK_MANAGER_H +#define FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASK_MANAGER_H + +#include +#include + +#include "actions/action.h" +#include "event_handler.h" +#include "tasks/task.h" + +namespace OHOS { +namespace SelectionFwk { + +using task_ptr_t = std::shared_ptr; +using action_ptr_t = std::unique_ptr; + +class TaskManager final { +private: + TaskManager(); + +public: + ~TaskManager() = default; + + TaskManager(const TaskManager &) = delete; + TaskManager(TaskManager &&) = delete; + TaskManager &operator=(const TaskManager &) = delete; + TaskManager &operator=(TaskManager &&) = delete; + + static TaskManager &GetInstance(); + + // Post a task to work thread + uint64_t PostTask(task_ptr_t task, uint32_t delayMs = 0); + + // Trigger task process async + void ProcessAsync(); + + // Resume paused task with seqId + void Complete(uint64_t seqId); + + // Pend an action to current task during executing + int32_t Pend(action_ptr_t action); + int32_t Pend(std::function); + + // Wait for task and execute + int32_t WaitExec(uint64_t seqId, uint32_t timeoutMs, std::function); + +private: + friend class SelectionAbility; + friend class TaskAmsInit; + void SetInited(bool flag); + +private: + void OnNewTask(task_ptr_t task); // Accept a new task + void Process(); // Process next task + void ProcessNextInnerTask(); // Process next inner task + void ProcessNextAmsTask(); // Process next AMS task + void ProcessNextImaTask(); // process next IMA task + void ProcessNextImsaTask(); // process next IMSA task + void ExecuteCurrentTask(); // Execute current task + + void Reset(); + +private: + bool inited_ { false }; + std::shared_ptr eventHandler_ { nullptr }; + + task_ptr_t curTask_ = { nullptr }; + std::list amsTasks_; + std::list imaTasks_; + std::list imsaTasks_; + std::list innerTasks_; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASK_MANAGER_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/tasks/task.h b/frameworks/native/selection_ability/include/tasks/task.h new file mode 100644 index 0000000000000000000000000000000000000000..9099a3366f4e695d320452ee327dad7980c0e12d --- /dev/null +++ b/frameworks/native/selection_ability/include/tasks/task.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2024-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 FRAMEWORKS_SELECTON_ABILITY_INCLUDE_TASKS_TASK_H +#define FRAMEWORKS_SELECTON_ABILITY_INCLUDE_TASKS_TASK_H + +#include "actions/action.h" + +#include +#include + +namespace OHOS { +namespace SelectionFwk { + +enum SourceType : uint32_t { + SOURCE_TYPE_AMS = 0, + SOURCE_TYPE_IMA, + SOURCE_TYPE_IMSA, + SOURCE_TYPE_INNER, +}; + +#define TASK_TYPE_OFFSET(src) ((src)*10000) + +enum TaskType : uint32_t { + // Task from AMS + TASK_TYPE_AMS_BEGIN = TASK_TYPE_OFFSET(SOURCE_TYPE_AMS), + TASK_TYPE_AMS_INIT = TASK_TYPE_AMS_BEGIN, + TASK_TYPE_AMS_END = TASK_TYPE_AMS_INIT, + + // Task from IMA + TASK_TYPE_IMA_BEGIN = TASK_TYPE_OFFSET(SOURCE_TYPE_IMA), + TASK_TYPE_IMA_SHOW_PANEL = TASK_TYPE_IMA_BEGIN, + TASK_TYPE_IMA_HIDE_PANEL, + TASK_TYPE_IMA_END = TASK_TYPE_IMA_HIDE_PANEL, + + // Task from IMSA + TASK_TYPE_IMSA_BEGIN = TASK_TYPE_OFFSET(SOURCE_TYPE_IMSA), + TASK_TYPE_IMSA_START_INPUT = TASK_TYPE_IMSA_BEGIN, + TASK_TYPE_IMSA_STOP_INPUT, + TASK_TYPE_IMSA_SHOW_KEYBOARD, + TASK_TYPE_IMSA_HIDE_KEYBOARD, + TASK_TYPE_IMSA_CLIENT_INACTIVE, + TASK_TYPE_IMSA_INIT_INPUT_CTRL_CHANNEL, + TASK_TYPE_IMSA_CURSOR_UPDATE, + TASK_TYPE_IMSA_SEND_PRIVATE_COMMAND, + TASK_TYPE_IMSA_SELECTION_CHANGE, + TASK_TYPE_IMSA_ATTRIBUTE_CHANGE, + TASK_TYPE_IMSA_STOP_INPUT_SERVICE, + TASK_TYPE_IMSA_SET_SUBPROPERTY, + TASK_TYPE_IMSA_SET_CORE_AND_AGENT, + TASK_TYPE_IMSA_ADJUST_KEYBOARD, + TASK_TYPE_IMSA_END = TASK_TYPE_IMSA_ADJUST_KEYBOARD, + + // Task from inner + TASK_TYPE_RESUME, +}; + +class Task { +public: + explicit Task(TaskType t); + Task(TaskType t, uint64_t seqId); + virtual ~Task() = default; + + RunningState Execute(); + RunningState Resume(uint64_t resumeId); + + virtual RunningState OnTask(std::shared_ptr task); + + int32_t Pend(std::shared_ptr task); + int32_t Pend(std::unique_ptr action); + + TaskType GetType() const; + SourceType GetSourceType() const; + uint64_t GetSeqId() const; + RunningState GetState() const; + bool IsRunning() const; + const std::list> &GetActions() const; + + static uint64_t GetNextSeqId(); + +private: + RunningState ExecuteInner(); + +protected: + const TaskType type_; + RunningState state_ { RUNNING_STATE_IDLE }; + const uint64_t seqId_; + std::unique_ptr curAction_ { nullptr }; + std::list> actions_; + std::list> pendingActions_; +}; + +} // namespace SelectionFwk +} // namespace OHOS +#endif // FRAMEWORKS_SELECTON_ABILITY_INCLUDE_TASKS_TASK_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/tasks/task_ams.h b/frameworks/native/selection_ability/include/tasks/task_ams.h new file mode 100644 index 0000000000000000000000000000000000000000..d0c336abcc46b5b628cde369200fe9084414d79b --- /dev/null +++ b/frameworks/native/selection_ability/include/tasks/task_ams.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024-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 FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_AMS_H +#define FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_AMS_H + +#include "task.h" + +#include "actions/action_wait.h" +#include "global.h" +#include "task_manager.h" + +namespace OHOS { +namespace SelectionFwk { +const uint32_t AMS_INIT_TIMEOUT_MS = 5000; + +class TaskAmsInit : public Task { +public: + TaskAmsInit() : Task(TASK_TYPE_AMS_INIT) + { + auto action = std::make_unique(seqId_, AMS_INIT_TIMEOUT_MS, + std::bind(&TaskAmsInit::OnComplete, this), std::bind(&TaskAmsInit::OnTimeout, this)); + actions_.push_back(std::move(action)); + } + ~TaskAmsInit() = default; + +private: + void OnComplete() + { + SELECTION_HILOGI("TaskAmsInit::OnComplete"); + TaskManager::GetInstance().SetInited(true); + } + void OnTimeout() + { + SELECTION_HILOGW("TaskAmsInit::OnTimeout"); + } +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_AMS_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/tasks/task_inner.h b/frameworks/native/selection_ability/include/tasks/task_inner.h new file mode 100644 index 0000000000000000000000000000000000000000..a2df33e35ceac2f5932ab9f3222f21aa7fd01595 --- /dev/null +++ b/frameworks/native/selection_ability/include/tasks/task_inner.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024-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 FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_INNER_H +#define FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_INNER_H + +#include "task.h" + +namespace OHOS { +namespace SelectionFwk { + +class TaskResume : public Task { +public: + explicit TaskResume(uint64_t seqId) : Task(TASK_TYPE_RESUME, seqId) { } + ~TaskResume() = default; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_INNER_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/include/tasks/task_ssa.h b/frameworks/native/selection_ability/include/tasks/task_ssa.h new file mode 100644 index 0000000000000000000000000000000000000000..727ff394a9f6eecdffe72e84aca0ce2f324bc47a --- /dev/null +++ b/frameworks/native/selection_ability/include/tasks/task_ssa.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2024-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 FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_SSA_H +#define FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_SSA_H + +#include "task.h" + +#include "input_attribute.h" +#include "input_client_info.h" +#include "selection_ability.h" +#include "secelction_property.h" +#include "iremote_object.h" + +namespace OHOS { +namespace SelectionFwk { + +class TaskSsaStartInput : public Task { +public: + TaskSsaStartInput(const InputClientInfo &client, bool fromClient) : Task(TASK_TYPE_IMSA_START_INPUT) + { + auto func = [client, fromClient]() { + SelectionAbility::GetInstance()->StartInput(client, fromClient); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaStartInput() = default; +}; + +class TaskSsaStopInput : public Task { +public: + explicit TaskSsaStopInput(sptr channel, uint32_t sessionId) : Task(TASK_TYPE_IMSA_STOP_INPUT) + { + auto func = [channel, sessionId]() { + SelectionAbility::GetInstance()->StopInput(channel, sessionId); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaStopInput() = default; +}; + +class TaskSsaShowKeyboard : public Task { +public: + TaskSsaShowKeyboard(int32_t requestKeyboardReason = 0) : Task(TASK_TYPE_IMSA_SHOW_KEYBOARD) + { + auto func = [requestKeyboardReason]() { + SelectionAbility::GetInstance()->ShowKeyboard(requestKeyboardReason); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaShowKeyboard() = default; +}; + +class TaskSsaHideKeyboard : public Task { +public: + explicit TaskSsaHideKeyboard() : Task(TASK_TYPE_IMSA_HIDE_KEYBOARD) + { + auto func = []() { + SelectionAbility::GetInstance()->HideKeyboard(); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaHideKeyboard() = default; +}; + +class TaskSsaOnClientInactive : public Task { +public: + explicit TaskSsaOnClientInactive(sptr channel) : Task(TASK_TYPE_IMSA_CLIENT_INACTIVE) + { + auto func = [channel]() { + SelectionAbility::GetInstance()->OnClientInactive(channel); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaOnClientInactive() = default; +}; + +class TaskSsaInitInputCtrlChannel : public Task { +public: + explicit TaskSsaInitInputCtrlChannel(sptr channel) : Task(TASK_TYPE_IMSA_INIT_INPUT_CTRL_CHANNEL) + { + auto func = [channel]() { + SelectionAbility::GetInstance()->OnInitInputControlChannel(channel); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaInitInputCtrlChannel() = default; +}; + +class TaskSsaOnCursorUpdate : public Task { +public: + TaskSsaOnCursorUpdate(int32_t x, int32_t y, int32_t h) : Task(TASK_TYPE_IMSA_CURSOR_UPDATE) + { + auto func = [x, y, h]() { + SelectionAbility::GetInstance()->OnCursorUpdate(x, y, h); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaOnCursorUpdate() = default; +}; + +class TaskSsaSendPrivateCommand : public Task { +public: + TaskSsaSendPrivateCommand(std::unordered_map privateCommand) + : Task(TASK_TYPE_IMSA_SEND_PRIVATE_COMMAND) + { + auto func = [privateCommand]() { + SelectionAbility::GetInstance()->ReceivePrivateCommand(privateCommand); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaSendPrivateCommand() = default; +}; + +class TaskSsaOnSelectionChange : public Task { +public: + TaskSsaOnSelectionChange(std::u16string text, int32_t oldBegin, int32_t oldEnd, int32_t newBegin, int32_t newEnd) + : Task(TASK_TYPE_IMSA_SELECTION_CHANGE) + { + auto func = [text, oldBegin, oldEnd, newBegin, newEnd]() { + SelectionAbility::GetInstance()->OnSelectionChange(text, oldBegin, oldEnd, newBegin, newEnd); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaOnSelectionChange() = default; +}; + +class TaskSsaAttributeChange : public Task { +public: + explicit TaskSsaAttributeChange(InputAttribute attr) : Task(TASK_TYPE_IMSA_ATTRIBUTE_CHANGE) + { + auto func = [attr]() { + SelectionAbility::GetInstance()->OnAttributeChange(attr); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaAttributeChange() = default; +}; + +class TaskSsaStopInputService : public Task { +public: + explicit TaskSsaStopInputService(bool isTerminateIme) : Task(TASK_TYPE_IMSA_STOP_INPUT_SERVICE) + { + auto func = [isTerminateIme]() { + SelectionAbility::GetInstance()->OnStopInputService(isTerminateIme); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaStopInputService() = default; +}; + +class TaskSsaOnSetSubProperty : public Task { +public: + explicit TaskSsaOnSetSubProperty(SubProperty prop) : Task(TASK_TYPE_IMSA_SET_SUBPROPERTY) + { + auto func = [prop]() { + SelectionAbility::GetInstance()->OnSetSubtype(prop); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaOnSetSubProperty() = default; +}; + +class TaskSsaSetCoreAndAgent : public Task { +public: + TaskSsaSetCoreAndAgent() : Task(TASK_TYPE_IMSA_SET_CORE_AND_AGENT) + { + auto func = []() { + SelectionAbility::GetInstance()->SetCoreAndAgent(); + }; + actions_.emplace_back(std::make_unique(func)); + } + ~TaskSsaSetCoreAndAgent() = default; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // FRAMEWORKS_SELECTION_ABILITY_INCLUDE_TASKS_TASK_SSA_H \ No newline at end of file diff --git a/frameworks/native/selection_ability/src/selection_ability.cpp b/frameworks/native/selection_ability/src/selection_ability.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5f9b5fda4448460460f2e5b94dfd27f9a0f9824 --- /dev/null +++ b/frameworks/native/selection_ability/src/selection_ability.cpp @@ -0,0 +1,128 @@ +/* + * 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 "selection_ability.h" + +#include +#include +#include "selection_panel.h" +#include "selection_log.h" +#include "selection_panel_manager.h" +#include "selection_app_validator.h" + +namespace OHOS { +namespace SelectionFwk { +sptr SelectionAbility::instance_; +std::mutex SelectionAbility::instanceLock_; + +SelectionAbility::SelectionAbility() { } + +SelectionAbility::~SelectionAbility() +{ + SELECTION_HILOGI("SelectionAbility::~SelectionAbility."); +} + +sptr SelectionAbility::GetInstance() +{ + if (instance_ == nullptr) { + std::lock_guard autoLock(instanceLock_); + if (instance_ == nullptr) { + SELECTION_HILOGI("SelectionAbility need new SA."); + instance_ = new (std::nothrow) SelectionAbility(); + if (instance_ == nullptr) { + SELECTION_HILOGE("instance is nullptr!"); + return instance_; + } + } + } + return instance_; +} + +int32_t SelectionAbility::CreatePanel(const std::shared_ptr &context, + const PanelInfo &panelInfo, std::shared_ptr &selectionPanel) +{ + SELECTION_HILOGI("enter CreatePanel, panelInfo: {panelType: %{public}d}", panelInfo.panelType); + std::ostringstream buffer; + panels_.ForEach([&](const PanelType &panelType, auto &) { + buffer << static_cast(panelType) << ","; + return false; + }); + auto panelTypes = buffer.str(); + SELECTION_HILOGI("panels_: %{public}s", panelTypes.c_str()); + + if (!SelectionAppValidator::GetInstance().Validate()) { + SELECTION_HILOGE("bundleName is not valid"); + return ErrorCode::ERROR_INVALID_OPERATION; + } + + int32_t result = ErrorCode::NO_ERROR; + auto flag = panels_.ComputeIfAbsent(panelInfo.panelType, + [&result, &panelInfo, &context, &selectionPanel] ( + const PanelType &panelType, std::shared_ptr &panel) { + selectionPanel = std::make_shared(); + result = selectionPanel->CreatePanel(context, panelInfo); + if (result == ErrorCode::NO_ERROR) { + panel = selectionPanel; + SelectionPanelManager::GetInstance().AddSelectionPanel(selectionPanel->GetWindowId(), selectionPanel); + return true; + } + selectionPanel = nullptr; + return false; + }); + return flag ? result : ErrorCode::ERROR_PARAMETER_CHECK_FAILED; +} + +int32_t SelectionAbility::DestroyPanel(const std::shared_ptr &selectionPanel) +{ + SELECTION_HILOGI("SelectionAbility DestroyPanel start."); + if (selectionPanel == nullptr) { + SELECTION_HILOGE("panel is nullptr!"); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + auto ret = selectionPanel->DestroyPanel(); + if (ret == ErrorCode::NO_ERROR) { + PanelType panelType = selectionPanel->GetPanelType(); + panels_.Erase(panelType); + } + return ret; +} + +int32_t SelectionAbility::ShowPanel(const std::shared_ptr &selectionpanel) +{ + if (selectionpanel == nullptr) { + return ErrorCode::ERROR_SELECTION_SERVICE; + } + auto ret = selectionpanel->ShowPanel(); + if (ret != ErrorCode::NO_ERROR) { + SELECTION_HILOGD("failed, ret: %{public}d", ret); + return ret; + } + return ErrorCode::NO_ERROR; +} + +int32_t SelectionAbility::HidePanel(const std::shared_ptr &selectionpanel) +{ + if (selectionpanel == nullptr) { + return ErrorCode::ERROR_SELECTION_SERVICE; + } + auto ret = selectionpanel->HidePanel(); + if (ret != ErrorCode::NO_ERROR) { + SELECTION_HILOGD("failed, ret: %{public}d", ret); + return ret; + } + return ErrorCode::NO_ERROR; +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/selection_ability/src/selection_app_validator.cpp b/frameworks/native/selection_ability/src/selection_app_validator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..22302fbc5eb74a1b3e7b938a5e7088ac0fedffd1 --- /dev/null +++ b/frameworks/native/selection_ability/src/selection_app_validator.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 "selection_app_validator.h" +#include "selection_log.h" + + +namespace OHOS::SelectionFwk { + +SelectionAppValidator& SelectionAppValidator::GetInstance() +{ + static SelectionAppValidator instance; + return instance; +} + +void SelectionAppValidator::SetValid() +{ + isValid_ = true; +} + +bool SelectionAppValidator::Validate() const +{ + return isValid_; +} +} // namespace OHOS::SelectionFwk \ No newline at end of file diff --git a/frameworks/native/selection_ability/src/selection_listener_impl.cpp b/frameworks/native/selection_ability/src/selection_listener_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a03e5a3858e7404328c6865008517467ca81057 --- /dev/null +++ b/frameworks/native/selection_ability/src/selection_listener_impl.cpp @@ -0,0 +1,67 @@ +/* + * 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 "selection_listener_impl.h" +#include "selection_log.h" +#include "selection_data_inner.h" +#include "selection_panel_manager.h" +#include "selection_ability.h" +#include "wm_common.h" +#include "window.h" + +namespace OHOS { +namespace SelectionFwk { + +static void CopySelectionData(const SelectionInfoData& src, SelectionInfo& dst) +{ + dst = src.data; +} + +ErrCode SelectionListenerImpl::OnSelectionChange(const SelectionInfoData& selectionInfoData) +{ + SELECTION_HILOGI("Recveive selection data length: %{public}u", selectionInfoData.data.text.length()); + SelectionInfo selectionInfo; + CopySelectionData(selectionInfoData, selectionInfo); + if (selectionI_ == nullptr) { + SELECTION_HILOGI("selectionI_ is nullptr"); + return 1; + } + selectionI_->OnSelectionEvent(selectionInfo); + return 0; +} + +ErrCode SelectionListenerImpl::FocusChange(const SelectionFocusChangeInfo& focusChangeInfo) +{ + SELECTION_HILOGI("Recveive FocusChange: %{public}s.", focusChangeInfo.ToString().c_str()); + if (!focusChangeInfo.isFocused_) { + return NO_ERROR; + } + auto selectionAppPid = getpid(); + if (selectionAppPid == focusChangeInfo.pid_) { + SELECTION_HILOGI("No need to hide or destory selection panel because window of selection app is focused."); + return NO_ERROR; + } + + auto& panelManager = SelectionPanelManager::GetInstance(); + if (!panelManager.FindWindowID(focusChangeInfo.windowId_)) { + SELECTION_HILOGI("The focus window is not a selection window, hide or destroy selection panels."); + panelManager.Dispose(); + return NO_ERROR; + } + + return NO_ERROR; +} +} // namespace SelectionFramework +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/selection_ability/src/selection_panel.cpp b/frameworks/native/selection_ability/src/selection_panel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1c8fb98e0c2dbad1faee7139a4b586b8643ea964 --- /dev/null +++ b/frameworks/native/selection_ability/src/selection_panel.cpp @@ -0,0 +1,389 @@ +/* + * 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 "selection_panel.h" + +#include +#include +#include + +#include "display_manager.h" +#include "scene_board_judgement.h" +#include "selection_log.h" +#include "selectionmethod_trace.h" +#include "selection_panel_manager.h" + +namespace OHOS { +namespace SelectionFwk { +using WMError = OHOS::Rosen::WMError; +using WindowState = OHOS::Rosen::WindowState; +using namespace Rosen; +using WindowGravity = OHOS::Rosen::WindowGravity; +using WindowState = OHOS::Rosen::WindowState; +std::atomic SelectionPanel::sequenceId_ { 0 }; +constexpr int32_t MAXWAITTIMES = 3; +constexpr int32_t WAITTIME = 100000; +std::mutex SelectionPanel::windowMutex_; +static std::map windowTypeToName = { + {PanelType::MAIN_PANEL, "SelectionMainFwkWindow"}, + {PanelType::MENU_PANEL, "SelectionMenuFwkWindow"} +}; + +SelectionPanel::~SelectionPanel() = default; +int32_t SelectionPanel::CreatePanel( + const std::shared_ptr &context, const PanelInfo &panelInfo) +{ + SELECTION_HILOGI("SelectionPanel CreatePanel start."); + panelType_ = panelInfo.panelType; + x_ = panelInfo.x; + y_ = panelInfo.y; + width_ = panelInfo.width; + height_ = panelInfo.height; + SELECTION_HILOGI( + "start , panelType/x/y/width/height: %{public}d/%{public}d/%{public}d/%{public}d/%{public}d.", + static_cast(panelType_), x_, y_, width_, height_); + + sptr baseOp = new Rosen::WindowOption(); + baseOp->SetWindowType(Rosen::WindowType::WINDOW_TYPE_SELECTION); + baseOp->SetWindowMode(Rosen::WindowMode::WINDOW_MODE_FLOATING); + OHOS::Rosen::Rect windowRect = {x_, y_, width_, height_}; + baseOp->SetWindowRect(windowRect); + WMError wmError = WMError::WM_OK; + auto windowName = windowTypeToName[panelInfo.panelType]; + sptr window = Rosen::Window::Create(windowName, baseOp, context, wmError); + SELECTION_HILOGI("Window creation returns %{public}d", wmError); + if (!window) { + SELECTION_HILOGE("Window creation failed"); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + window_ = window; + SELECTION_HILOGI("Window::Create Success"); + return 0; +} + +std::string SelectionPanel::GeneratePanelName() +{ + uint32_t sequenceId = GenerateSequenceId(); + std::string windowName = panelType_ == PanelType::MENU_PANEL ? "menuPanel" + std::to_string(sequenceId) : + "mainPanel" + std::to_string(sequenceId); + SELECTION_HILOGD("SelectionPanel, windowName: %{public}s.", windowName.c_str()); + return windowName; +} + +int32_t SelectionPanel::SetPanelProperties() +{ + if (window_ == nullptr) { + SELECTION_HILOGE("window is nullptr!"); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + WindowGravity gravity = WindowGravity::WINDOW_GRAVITY_FLOAT; + if (!isScbEnable_) { + WMError wmError = window_->SetWindowGravity(gravity, invalidGravityPercent); + if (wmError != WMError::WM_OK) { + SELECTION_HILOGE("failed to set window gravity, wmError is %{public}d, start destroy window!", wmError); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + return ErrorCode::NO_ERROR; + } + keyboardLayoutParams_.gravity_ = gravity; + auto ret = window_->AdjustKeyboardLayout(keyboardLayoutParams_); + if (ret != WMError::WM_OK) { + SELECTION_HILOGE("SetWindowGravity failed, wmError is %{public}d, start destroy window!", ret); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + return ErrorCode::NO_ERROR; +} + +int32_t SelectionPanel::DestroyPanel() +{ + if (windowStatus_ == SelectionWindowStatus::DESTROYED) { + SELECTION_HILOGE("window has destroyed!"); + return ErrorCode::NO_ERROR; + } + auto ret = HidePanel(); + if (ret != ErrorCode::NO_ERROR) { + SELECTION_HILOGE("SelectionPanel, hide panel failed, ret: %{public}d!", ret); + } + if (window_ == nullptr) { + SELECTION_HILOGE("window_ is nullptr!"); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + auto result = window_->Destroy(); + window_ = nullptr; + SELECTION_HILOGI("destroy ret: %{public}d", result); + windowStatus_ = SelectionWindowStatus::DESTROYED; + PanelStatusChange(SelectionWindowStatus::DESTROYED); + return ErrorCode::NO_ERROR; +} + +uint32_t SelectionPanel::GenerateSequenceId() +{ + uint32_t seqId = sequenceId_.fetch_add(1, std::memory_order_seq_cst); + return seqId % std::numeric_limits::max(); +} + +int32_t SelectionPanel::SetUiContent(const std::string &contentInfo, napi_env env) +{ + if (window_ == nullptr) { + SELECTION_HILOGE("window_ is nullptr, can not SetUiContent!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + if (IsDestroyed()) { + SELECTION_HILOGE("window is destroyed!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + WMError ret = WMError::WM_OK; + + ret = window_->NapiSetUIContent(contentInfo, env, nullptr); + WMError wmError = window_->SetTransparent(true); + if (isWaitSetUiContent_.load()) { + isWaitSetUiContent_.store(false); + } + SELECTION_HILOGI("SetTransparent ret: %{public}u.", wmError); + SELECTION_HILOGI("NapiSetUIContent ret: %{public}d.", ret); + return ret == WMError::WM_ERROR_INVALID_PARAM ? ErrorCode::ERROR_PARAMETER_CHECK_FAILED : ErrorCode::NO_ERROR; +} + +PanelType SelectionPanel::GetPanelType() +{ + return panelType_; +} + +int32_t SelectionPanel::ShowPanel() +{ + SELECTION_HILOGD("SelectionPanel start."); + if (window_ == nullptr) { + SELECTION_HILOGE("window_ is nullptr!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + if (IsDestroyed()) { + SELECTION_HILOGE("window is destroyed!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + int32_t iCounter = 0; + while (isWaitSetUiContent_.load() && iCounter < MAXWAITTIMES) { + usleep(WAITTIME); + iCounter++; + SELECTION_HILOGI("SelectionPanel show pannel waitTime %{public}d.", iCounter); + } + + if (isWaitSetUiContent_.load()) { + SELECTION_HILOGE("isWaitSetUiContent_ is true!"); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + + if (IsShowing()) { + SELECTION_HILOGI("panel already shown."); + return ErrorCode::NO_ERROR; + } + auto ret = WMError::WM_OK; + { + SelectionMethodSyncTrace tracer("SelectionPanel_ShowPanel"); + ret = window_->Show(0, false, false); + } + if (ret != WMError::WM_OK) { + SELECTION_HILOGE("ShowPanel error, err = %{public}d", ret); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + SELECTION_HILOGI("Selection panel shown successfully."); + windowStatus_ = SelectionWindowStatus::NONE; + return ErrorCode::NO_ERROR; +} + +bool SelectionPanel::IsShowing() +{ + if (window_ == nullptr) { + SELECTION_HILOGE("window_ is nullptr!"); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + auto windowState = window_->GetWindowState(); + if (windowState == WindowState::STATE_SHOWN) { + return true; + } + SELECTION_HILOGD("windowState: %{public}d.", static_cast(windowState)); + return false; +} + +void SelectionPanel::PanelStatusChange(const SelectionWindowStatus &status) +{ + if (panelStatusListener_ == nullptr) { + SELECTION_HILOGE("panelStatusListener_ is nullptr."); + return; + } + + auto itr = panelStatusMap_.find(status); + if (itr == panelStatusMap_.end()) { + SELECTION_HILOGE("wrong status."); + return; + } + + if (status == SelectionWindowStatus::HIDDEN && hiddenRegistered_) { + panelStatusListener_->OnPanelStatus(windowId_, itr->second); + } + if (status == SelectionWindowStatus::DESTROYED && destroyedRegistered_) { + panelStatusListener_->OnPanelStatus(windowId_, itr->second); + } +} + +int32_t SelectionPanel::HidePanel() +{ + SELECTION_HILOGD("SelectionPanel start"); + if (windowStatus_ == SelectionWindowStatus::HIDDEN) { + SELECTION_HILOGE("window has hidden!"); + return ErrorCode::NO_ERROR; + } + + if (window_ == nullptr) { + SELECTION_HILOGE("window_ is nullptr!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + if (IsDestroyed()) { + SELECTION_HILOGE("window is destroyed!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + if (IsHidden()) { + SELECTION_HILOGI("panel already hidden."); + return ErrorCode::NO_ERROR; + } + auto ret = WMError::WM_OK; + { + SelectionMethodSyncTrace tracer("SelectionPanel_HidePanel"); + ret = window_->Hide(); + } + if (ret != WMError::WM_OK) { + SELECTION_HILOGE("HidePanel error, err: %{public}d!", ret); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + SELECTION_HILOGI("success, panelType/x/y/width/height: %{public}d/%{public}d/%{public}d/%{public}d/%{public}d.", + static_cast(panelType_), x_, y_, width_, height_); + windowStatus_ = SelectionWindowStatus::HIDDEN; + PanelStatusChange(SelectionWindowStatus::HIDDEN); + return ErrorCode::NO_ERROR; +} + +bool SelectionPanel::IsHidden() +{ + auto windowState = window_->GetWindowState(); + if (windowState == WindowState::STATE_HIDDEN) { + return true; + } + SELECTION_HILOGD("windowState: %{public}d.", static_cast(windowState)); + return false; +} + +bool SelectionPanel::IsDestroyed() const +{ + return window_ && window_->GetWindowState() == WindowState::STATE_DESTROYED; +} + +int32_t SelectionPanel::StartMoving() +{ + if (window_ == nullptr) { + SELECTION_HILOGE("window_ is nullptr!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + if (IsDestroyed()) { + SELECTION_HILOGE("window is destroyed!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + auto ret = window_->StartMoveWindow(); + if (ret == WmErrorCode::WM_ERROR_DEVICE_NOT_SUPPORT) { + SELECTION_HILOGE("window manager service not support error ret = %{public}d.", ret); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + if (ret != WmErrorCode::WM_OK) { + SELECTION_HILOGE("window manager service error ret = %{public}d.", ret); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + SELECTION_HILOGI("StartMoving success!"); + return ErrorCode::NO_ERROR; +} + +int32_t SelectionPanel::MoveTo(int32_t x, int32_t y) +{ + SELECTION_HILOGD("moveto start!"); + if (window_ == nullptr) { + SELECTION_HILOGE("window_ is nullptr!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + if (IsDestroyed()) { + SELECTION_HILOGE("window is destroyed!"); + return ErrorCode::ERROR_PANEL_DESTROYED; + } + auto ret = window_->MoveTo(x, y); + SELECTION_HILOGI("x/y: %{public}d/%{public}d, ret = %{public}d", x, y, ret); + return ret == WMError::WM_ERROR_INVALID_PARAM ? ErrorCode::ERROR_PARAMETER_CHECK_FAILED : ErrorCode::NO_ERROR; +} + +bool SelectionPanel::SetPanelStatusListener(std::shared_ptr statusListener, + const std::string &type) +{ + if (!MarkListener(type, true)) { + return false; + } + SELECTION_HILOGD("type: %{public}s.", type.c_str()); + if (panelStatusListener_ == nullptr) { + auto isExist = [&type](const auto& pair) { return pair.second == type; }; + if (std::find_if(panelStatusMap_.begin(), panelStatusMap_.end(), isExist) != panelStatusMap_.end()) { + SELECTION_HILOGD("panelStatusListener_ is nullptr, need to be set"); + panelStatusListener_ = std::move(statusListener); + } + } + return true; +} + +bool SelectionPanel::MarkListener(const std::string &type, bool isRegister) +{ + if (panelStatusMap_.find(SelectionWindowStatus::DESTROYED) != panelStatusMap_.end() && + type == panelStatusMap_.at(SelectionWindowStatus::DESTROYED)) { + destroyedRegistered_ = isRegister; + } else if (panelStatusMap_.find(SelectionWindowStatus::HIDDEN) != panelStatusMap_.end() && + type == panelStatusMap_.at(SelectionWindowStatus::HIDDEN)) { + hiddenRegistered_ = isRegister; + } else { + SELECTION_HILOGE("type error!"); + return false; + } + return true; +} + +void SelectionPanel::ClearPanelListener(const std::string &type) +{ + if (!MarkListener(type, false)) { + return; + } + SELECTION_HILOGD("type: %{public}s.", type.c_str()); + if (panelStatusListener_ == nullptr) { + SELECTION_HILOGD("panelStatusListener_ not set, don't need to remove."); + return; + } + if (IsPanelListenerClearable()) { + return; + } + panelStatusListener_ = nullptr; +} + +bool SelectionPanel::IsPanelListenerClearable() +{ + return (destroyedRegistered_ || hiddenRegistered_); +} + +uint32_t SelectionPanel::GetWindowId() +{ + std::lock_guard lock(windowMutex_); + return window_->GetWindowId(); +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/selection_ability/src/selection_panel_manager.cpp b/frameworks/native/selection_ability/src/selection_panel_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..20fc5ea891a9470de09d0c891a54a546e770f2c1 --- /dev/null +++ b/frameworks/native/selection_ability/src/selection_panel_manager.cpp @@ -0,0 +1,73 @@ +/* + * 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 "selection_panel_manager.h" +#include "selection_ability.h" +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { + +SelectionPanelManager& SelectionPanelManager::GetInstance() +{ + static SelectionPanelManager instance; + return instance; +} + +void SelectionPanelManager::AddSelectionPanel(uint32_t id, std::shared_ptr &obj) +{ + std::lock_guard lock(mutex_); + if (obj == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr"); + return; + } + storage_[id] = obj; +} + +std::shared_ptr SelectionPanelManager::GetSelectionPanel(uint32_t id) const +{ + std::lock_guard lock(mutex_); + auto it = storage_.find(id); + return (it != storage_.end()) ? it->second : nullptr; +} + +bool SelectionPanelManager::FindWindowID(uint32_t id) const +{ + std::lock_guard lock(mutex_); + return storage_.find(id) != storage_.end(); +} + +void SelectionPanelManager::Dispose() +{ + std::lock_guard lock(mutex_); + for (auto iter = storage_.begin(); iter != storage_.end();) { + auto selectionPanel = iter->second; + if (selectionPanel == nullptr) { + SELECTION_HILOGE("selectionPanel is nullptr"); + ++iter; + continue; + } + if (selectionPanel->GetPanelType() == PanelType::MENU_PANEL) { + selectionPanel->HidePanel(); + } else if (selectionPanel->GetPanelType() == PanelType::MAIN_PANEL) { + SelectionAbility::GetInstance()->DestroyPanel(selectionPanel); + iter = storage_.erase(iter); + continue; + } + ++iter; + } +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/selection_ability/src/task_manager.cpp b/frameworks/native/selection_ability/src/task_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1707710a2767c6a14825842002a352f77da1ee08 --- /dev/null +++ b/frameworks/native/selection_ability/src/task_manager.cpp @@ -0,0 +1,278 @@ +/* + * 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 "task_manager.h" + +#include +#include "actions/action.h" +#include "actions/action_wait.h" +#include "selection_log.h" +#include "tasks/task.h" +#include "tasks/task_inner.h" + +namespace OHOS { +namespace SelectionFwk { + +static const std::string THREAD_NAME = "OS_sf_task_manager"; + +TaskManager::TaskManager() +{ + auto runner = AppExecFwk::EventRunner::Create(THREAD_NAME); + eventHandler_ = std::make_shared(runner); +} + +TaskManager &TaskManager::GetInstance() +{ + static TaskManager instance; + return instance; +} + +uint64_t TaskManager::PostTask(task_ptr_t task, uint32_t delayMs) +{ + if (!task) { + SELECTION_HILOGE("task is NULL!"); + return 0; + } + + auto func = std::bind(&TaskManager::OnNewTask, this, task); + eventHandler_->PostTask(func, __FUNCTION__, delayMs); + return task->GetSeqId(); +} + +void TaskManager::ProcessAsync() +{ + auto func = [=] { + Process(); + }; + eventHandler_->PostTask(func, __FUNCTION__); +} + +void TaskManager::Complete(uint64_t seqId) +{ + PostTask(std::make_shared(seqId)); +} + +int32_t TaskManager::Pend(action_ptr_t action) +{ + if (action == nullptr) { + SELECTION_HILOGE("curTask_ is NULL or not runing, pend failed!"); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + if (curTask_ == nullptr || !curTask_->IsRunning()) { + SELECTION_HILOGE("curTask_ is NULL or not runing, pend failed!"); + return ErrorCode::ERROR_SELECTION_SERVICE; + } + return curTask_->Pend(std::move(action)); +} + +int32_t TaskManager::Pend(std::function func) +{ + return Pend(std::make_unique(func)); +} + +int32_t TaskManager::WaitExec(uint64_t seqId, uint32_t timeoutMs, std::function func) +{ + auto wait = std::make_unique(seqId, timeoutMs); + int32_t ret = Pend(std::move(wait)); + if (ret != ErrorCode::NO_ERROR) { + SELECTION_HILOGE("Pend ActionWait failed, ret=%{public}d", ret); + return ret; + } + + auto exec = std::make_unique(func); + ret = Pend(std::move(exec)); + if (ret != ErrorCode::NO_ERROR) { + SELECTION_HILOGE("Pend Action failed, ret=%{public}d", ret); + return ret; + } + return ErrorCode::NO_ERROR; +} + +void TaskManager::SetInited(bool flag) +{ + inited_ = flag; +} + +void TaskManager::OnNewTask(task_ptr_t task) +{ + if (task == nullptr) { + SELECTION_HILOGE("task is NULL!"); + return; + } + auto srcType = task->GetSourceType(); + switch (srcType) { + case SOURCE_TYPE_AMS: + amsTasks_.push_back(task); + break; + case SOURCE_TYPE_IMA: + imaTasks_.push_back(task); + break; + case SOURCE_TYPE_IMSA: + imsaTasks_.push_back(task); + break; + case SOURCE_TYPE_INNER: + innerTasks_.push_back(task); + break; + default: + SELECTION_HILOGE("task type %{public}d unknown!", srcType); + return; + } + Process(); +} + +void TaskManager::Process() +{ + ProcessNextInnerTask(); + ProcessNextAmsTask(); + ProcessNextImaTask(); + ProcessNextImsaTask(); +} + +void TaskManager::ProcessNextInnerTask() +{ + while (curTask_) { + // curTask_ is not NULL, it must be paused + // Loop through innerTasks_, try resume + if (innerTasks_.empty()) { + SELECTION_HILOGI("InnerTasks_ empty, return"); + return; + } + + auto task = innerTasks_.front(); + innerTasks_.pop_front(); + auto state = curTask_->OnTask(task); + if (state == RUNNING_STATE_COMPLETED) { + // current task completed + curTask_.reset(); + innerTasks_.clear(); + return; + } + if (state == RUNNING_STATE_PAUSED) { + // current task still paused, try next inner task + continue; + } + + // unreachable + SELECTION_HILOGE("Unexpected OnTask result %{public}d", state); + curTask_.reset(); + innerTasks_.clear(); + } +} + +void TaskManager::ProcessNextAmsTask() +{ + if (amsTasks_.empty()) { + return; + } + + // AMS task has higher priority. If curTask_ is valid and not from AMS, drop it + if (curTask_) { + if (curTask_->GetSourceType() == SOURCE_TYPE_AMS) { + return; + } + curTask_.reset(); + } + + while (!curTask_) { + if (amsTasks_.empty()) { + return; + } + curTask_ = amsTasks_.front(); + amsTasks_.pop_front(); + ExecuteCurrentTask(); + } +} + +void TaskManager::ProcessNextImaTask() +{ + if (imaTasks_.empty()) { + return; + } + + if (curTask_) { + // curTask_ must be paused here + while (!imaTasks_.empty()) { + auto task = imaTasks_.front(); + imaTasks_.pop_front(); + auto state = curTask_->OnTask(task); + if (state == RUNNING_STATE_COMPLETED) { + curTask_.reset(); + break; + } + } + } + + while (!curTask_) { + if (imaTasks_.empty()) { + return; + } + curTask_ = imaTasks_.front(); + imaTasks_.pop_front(); + ExecuteCurrentTask(); + } +} + +void TaskManager::ProcessNextImsaTask() +{ + if (!inited_) { + return; + } + + if (curTask_ || imsaTasks_.empty()) { + return; + } + + while (!curTask_) { + if (imsaTasks_.empty()) { + return; + } + curTask_ = imsaTasks_.front(); + imsaTasks_.pop_front(); + ExecuteCurrentTask(); + } +} + +void TaskManager::ExecuteCurrentTask() +{ + if (curTask_ == nullptr) { + return; + } + auto state = curTask_->Execute(); + if (state == RUNNING_STATE_COMPLETED) { + SELECTION_HILOGI("curTask_ completed"); + curTask_.reset(); + ProcessAsync(); + return; + } + if (state == RUNNING_STATE_PAUSED) { + SELECTION_HILOGI("curTask_ paused"); + return; + } + SELECTION_HILOGE("Unexpected Execute result %{public}u", state); + curTask_.reset(); +} + +void TaskManager::Reset() +{ + inited_ = false; + curTask_ = nullptr; + innerTasks_.clear(); + imaTasks_.clear(); + imsaTasks_.clear(); + amsTasks_.clear(); +} + +} // namespace SelectionFwk +} // namespace OHOS diff --git a/frameworks/native/selection_ability/src/tasks/task.cpp b/frameworks/native/selection_ability/src/tasks/task.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a13d78a8817ba0e75e6b5db4e2f02b01b81fd12c --- /dev/null +++ b/frameworks/native/selection_ability/src/tasks/task.cpp @@ -0,0 +1,169 @@ +/* + * 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 "tasks/task.h" +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { + +Task::Task(TaskType type) : type_(type), seqId_(GetNextSeqId()) { } + +Task::Task(TaskType type, uint64_t seqId) : type_(type), seqId_(seqId) { } + +RunningState Task::Execute() +{ + if (state_ != RUNNING_STATE_IDLE) { + SELECTION_HILOGE("Task not runnable, state=%{public}u", state_); + return RUNNING_STATE_ERROR; + } + return ExecuteInner(); +} + +RunningState Task::Resume(uint64_t seqId) +{ + if (!curAction_) { + SELECTION_HILOGE("curAction_ is NULL, error!"); + return RUNNING_STATE_ERROR; + } + + if (state_ != RUNNING_STATE_PAUSED) { + SELECTION_HILOGE("state_ is %{public}u, do not need to resume", state_); + return RUNNING_STATE_ERROR; + } + + auto ret = curAction_->Resume(seqId); + if (ret == RUNNING_STATE_PAUSED) { // resume failed, return + return state_; + } + if (ret == RUNNING_STATE_COMPLETED) { // resume success, continue to execute + curAction_.reset(); + return ExecuteInner(); + } + + // unreachable + SELECTION_HILOGE("curAction_ resume return %{public}u, error!", ret); + return RUNNING_STATE_ERROR; +} + +RunningState Task::OnTask(std::shared_ptr task) +{ + if (task == nullptr) { + SELECTION_HILOGE("task is NULL, error!"); + return state_; + } + auto src = task->GetSourceType(); + if (src == SOURCE_TYPE_INNER) { + return Resume(task->GetSeqId()); + } + if (src == SOURCE_TYPE_IMA) { + Pend(task); + return state_; + } + return state_; +} + +TaskType Task::GetType() const +{ + return type_; +} + +SourceType Task::GetSourceType() const +{ + if (type_ >= TASK_TYPE_AMS_BEGIN && type_ <= TASK_TYPE_AMS_END) { + return SOURCE_TYPE_AMS; + } + if (type_ >= TASK_TYPE_IMA_BEGIN && type_ <= TASK_TYPE_IMA_END) { + return SOURCE_TYPE_IMA; + } + if (type_ >= TASK_TYPE_IMSA_BEGIN && type_ <= TASK_TYPE_IMSA_END) { + return SOURCE_TYPE_IMSA; + } + return SOURCE_TYPE_INNER; +} + +uint64_t Task::GetSeqId() const +{ + return seqId_; +} + +RunningState Task::GetState() const +{ + return state_; +} + +bool Task::IsRunning() const +{ + return state_ == RUNNING_STATE_RUNNING; +} + +const std::list> &Task::GetActions() const +{ + return actions_; +} + +uint64_t Task::GetNextSeqId() +{ + static std::atomic maxSeqId(1); + return maxSeqId.fetch_add(1); +} + +RunningState Task::ExecuteInner() +{ + state_ = RUNNING_STATE_RUNNING; + if (!pendingActions_.empty()) { + pendingActions_.splice(pendingActions_.end(), actions_); + actions_.swap(pendingActions_); + } + + while (!actions_.empty()) { + curAction_ = std::move(actions_.front()); + actions_.pop_front(); + + auto ret = curAction_->Execute(); + + // check pending tasks + if (!pendingActions_.empty()) { + pendingActions_.splice(pendingActions_.end(), actions_); + actions_.swap(pendingActions_); + } + + if (ret == RUNNING_STATE_COMPLETED) { + curAction_.reset(); + continue; + } + + state_ = RUNNING_STATE_PAUSED; + return state_; + } + state_ = RUNNING_STATE_COMPLETED; + return state_; +} + +int32_t Task::Pend(std::shared_ptr task) +{ + pendingActions_.splice(pendingActions_.end(), task->actions_); + actions_.swap(pendingActions_); + return ErrorCode::NO_ERROR; +} + +int32_t Task::Pend(std::unique_ptr action) +{ + pendingActions_.push_back(std::move(action)); + return ErrorCode::NO_ERROR; +} + +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/native/selection_client/selection_client.cpp b/frameworks/native/selection_client/selection_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b73799179a31c8e0b998aabf0e825dfbcfb53f08 --- /dev/null +++ b/frameworks/native/selection_client/selection_client.cpp @@ -0,0 +1,50 @@ +/* + * 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 "selection_client.h" + +#include "iselection_service.h" +#include "iservice_registry.h" +#include "selection_log.h" +#include "system_ability_definition.h" + +using namespace OHOS; +using namespace OHOS::SelectionFwk; + +SelectionClient& SelectionClient::GetInstance() +{ + static SelectionClient instance; + return instance; +} + +bool SelectionClient::IsCurrentSelectionApp(int pid) +{ + SELECTION_HILOGI("SelectionClient::IsCurrentSelectionApp"); + bool result = false; + auto systemAbilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (systemAbilityManager == nullptr) { + SELECTION_HILOGE("system ability manager is nullptr!"); + return result; + } + sptr systemAbility = nullptr; + systemAbility = systemAbilityManager->GetSystemAbility(SELECTION_FWK_SA_ID); + if (systemAbility == nullptr) { + SELECTION_HILOGE("get system ability is nullptr!"); + return result; + } + auto abilityManager = iface_cast(systemAbility); + abilityManager->IsCurrentSelectionApp(pid, result); + return result; +} \ No newline at end of file diff --git a/frameworks/native/selection_extension/BUILD.gn b/frameworks/native/selection_extension/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..7b50d9cee596e64478ac5084eee79df4005e147c --- /dev/null +++ b/frameworks/native/selection_extension/BUILD.gn @@ -0,0 +1,68 @@ +# 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("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + + +ohos_shared_library("selection_extension_ability_native") { + + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + integer_overflow = true + ubsan = true + } + + cflags_cc = [ + "-fdata-sections", + "-ffunction-sections", + "-Os", + ] + + include_dirs = [ + "include", + "${selection_fwk_root_path}/utils/include", + ] + + sources = [ + "src/selection_extension.cpp", + "src/selection_extension_context.cpp", + "src/selection_extension_module_loader.cpp", + "src/js_selection_extension.cpp", + "src/js_selection_extension_context.cpp", + ] + + + deps = [ + "${selection_fwk_root_path}/common:selection_common" + ] + + external_deps = [ + "ability_base:want", + "ability_runtime:ability_context_native", + "ability_runtime:ability_manager", + "ability_runtime:app_context", + "ability_runtime:napi_common", + "ability_runtime:runtime", + "c_utils:utils", + "ipc:ipc_napi", + "napi:ace_napi", + "hilog:libhilog", + ] + + relative_install_dir = "extensionability/" + subsystem_name = "systemabilitymgr" + part_name = "selectionfwk" +} diff --git a/frameworks/native/selection_extension/include/js_selection_extension.h b/frameworks/native/selection_extension/include/js_selection_extension.h new file mode 100644 index 0000000000000000000000000000000000000000..8101aad1dfde91b4d25a5c149136cb8b13131728 --- /dev/null +++ b/frameworks/native/selection_extension/include/js_selection_extension.h @@ -0,0 +1,76 @@ +/* + * 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 JS_SELECTION_EXTENSION_H +#define JS_SELECTION_EXTENSION_H +#include "selection_extension.h" + +namespace OHOS::AbilityRuntime { +class JsRuntime; + +class JsSelectionExtension : public SelectionExtension { +public: + explicit JsSelectionExtension(JsRuntime& jsRuntime); + virtual ~JsSelectionExtension() override; + /** + * @brief Create JsSelectionExtension. + * + * @param runtime The runtime. + * @return The JsSelectionExtension instance. + */ + static JsSelectionExtension* Create(const std::unique_ptr& runtime); + /** + * @brief Init the extension. + * + * @param record the extension record. + * @param application the application info. + * @param handler the extension handler. + * @param token the remote token. + */ + virtual void Init(const std::shared_ptr& record, + const std::shared_ptr& application, + std::shared_ptr& handler, + const sptr& token) override; + + /** + * @brief Called when this Service extension is connected for the first time. + * + * You can override this function to implement your own processing logic. + * + * @param want Indicates the {@link Want} structure containing connection information about the Service extension. + * @return Returns a pointer to the sid of the connected Service extension. + */ + virtual sptr OnConnect(const AAFwk::Want& want) override; + + /** + * @brief Called when all abilities connected to this Service extension are disconnected. + * + * You can override this function to implement your own processing logic. + * + */ + virtual void OnDisconnect(const AAFwk::Want& want) override; + +private: + napi_value CallObjectMethod(const char* methodName, const napi_value* argv = nullptr, size_t argc = 0); + void GetSrcPath(std::string& srcPath); + void BindContext(napi_env env, napi_value obj); + +private: + JsRuntime& jsRuntime_; + std::unique_ptr jsObj_; +}; +} // namespace OHOS::AbilityRuntime + +#endif // JS_SELECTION_EXTENSION_H \ No newline at end of file diff --git a/frameworks/native/selection_extension/include/js_selection_extension_context.h b/frameworks/native/selection_extension/include/js_selection_extension_context.h new file mode 100644 index 0000000000000000000000000000000000000000..c79ee908e63967bae924087ad334bde581d61602 --- /dev/null +++ b/frameworks/native/selection_extension/include/js_selection_extension_context.h @@ -0,0 +1,27 @@ +/* + * 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 JS_SELECTION_EXTENSION_CONTEXT_H +#define JS_SELECTION_EXTENSION_CONTEXT_H + +#include +#include "native_engine/native_engine.h" +#include "selection_extension_context.h" + +namespace OHOS::AbilityRuntime { +napi_value CreateJsSelectionExtensionContext(napi_env env, std::shared_ptr context); +} + +#endif // JS_SELECTION_EXTENSION_CONTEXT_H diff --git a/frameworks/native/selection_extension/include/selection_extension.h b/frameworks/native/selection_extension/include/selection_extension.h new file mode 100644 index 0000000000000000000000000000000000000000..d983eda8a071ed6c54a775cdde8dd6ff3026ddfb --- /dev/null +++ b/frameworks/native/selection_extension/include/selection_extension.h @@ -0,0 +1,51 @@ +/* + * 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 SELECTION_EXTENSION_H +#define SELECTION_EXTENSION_H + +#include "extension_base.h" +#include "selection_extension_context.h" + +namespace OHOS::AbilityRuntime { +class SelectionExtension : public ExtensionBase { +public: + SelectionExtension() = default; + virtual ~SelectionExtension() = default; + + /** + * @brief Create Extension. + * + * @param runtime The runtime. + * @return The InputMethodExtension instance. + */ + static SelectionExtension* Create(const std::unique_ptr& runtime); + + /** + * @brief Init the extension. + * + * @param record the extension record. + * @param application the application info. + * @param handler the extension handler. + * @param token the remote token. + */ + virtual void Init(const std::shared_ptr& record, + const std::shared_ptr& application, + std::shared_ptr& handler, + const sptr& token) override; +}; +} // namespace OHOS::AbilityRuntime + +#endif \ No newline at end of file diff --git a/frameworks/native/selection_extension/include/selection_extension_context.h b/frameworks/native/selection_extension/include/selection_extension_context.h new file mode 100644 index 0000000000000000000000000000000000000000..6ea410f48727df5baa250c0987016c66c3064803 --- /dev/null +++ b/frameworks/native/selection_extension/include/selection_extension_context.h @@ -0,0 +1,53 @@ +/* + * 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 SELECTION_EXTENSION_CONTEXT_H +#define SELECTION_EXTENSION_CONTEXT_H + +#include "extension_context.h" +#include "start_options.h" +#include "want.h" + +namespace OHOS::AbilityRuntime { +class SelectionExtensionContext : public ExtensionContext { +public: + SelectionExtensionContext() = default; + virtual ~SelectionExtensionContext() = default; + + /** + * @brief Starts a new ability. + * An ability using the AbilityInfo.AbilityType.SERVICE or AbilityInfo.AbilityType.PAGE template uses this method + * to start a specific ability. The system locates the target ability from installed abilities based on the value + * of the want parameter and then starts it. You can specify the ability to start using the want parameter. + * + * @param want Indicates the Want containing information about the target ability to start. + * + * @return errCode ERR_OK on success, others on failure. + */ + ErrCode StartAbility(const AAFwk::Want& want) const; + + static const size_t CONTEXT_TYPE_ID; + +protected: + bool IsContext(size_t contextTypeId) override + { + return contextTypeId == CONTEXT_TYPE_ID || ExtensionContext::IsContext(contextTypeId); + } + +private: + static int ILLEGAL_REQUEST_CODE; +}; +} // namespace OHOS::AbilityRuntime +#endif // SELECTION_EXTENSION_CONTEXT_H diff --git a/frameworks/native/selection_extension/include/selection_extension_module_loader.h b/frameworks/native/selection_extension/include/selection_extension_module_loader.h new file mode 100644 index 0000000000000000000000000000000000000000..65cb68ca2d87e188fcc404354c63d835d6f4ef78 --- /dev/null +++ b/frameworks/native/selection_extension/include/selection_extension_module_loader.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 SELECTION_EXTENSION_MODULE_LOADER_H +#define SELECTION_EXTENSION_MODULE_LOADER_H + +#include "extension_module_loader.h" + +namespace OHOS::AbilityRuntime { +class SelectionExtensionModuleLoader : public ExtensionModuleLoader, public Singleton { + DECLARE_SINGLETON(SelectionExtensionModuleLoader); + +public: + /** + * @brief Create Extension. + * + * @param runtime The runtime. + * @return The Extension instance. + */ + virtual Extension* Create(const std::unique_ptr& runtime) const override; + + /** + * @brief Get Extension config params. + * + * @return The Extension config params. + */ + virtual std::map GetParams() override; +}; +} // namespace OHOS::AbilityRuntime + +#endif \ No newline at end of file diff --git a/frameworks/native/selection_extension/src/js_selection_extension.cpp b/frameworks/native/selection_extension/src/js_selection_extension.cpp new file mode 100644 index 0000000000000000000000000000000000000000..36e8bac5e473b0468896b8d42350422b0ac69c71 --- /dev/null +++ b/frameworks/native/selection_extension/src/js_selection_extension.cpp @@ -0,0 +1,257 @@ +/* + * 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 "js_selection_extension.h" +#include "js_runtime.h" +#include "js_runtime_utils.h" +#include "js_selection_extension_context.h" +#include "napi/native_api.h" +#include "napi_common_want.h" +#include "napi_remote_object.h" +#include "selection_log.h" + +namespace OHOS::AbilityRuntime { +namespace { +constexpr size_t ARGC_ONE = 1; +} + +napi_value AttachSelectionExtensionContext(napi_env env, void* value, void*) +{ + SELECTION_HILOGI("AttachSelectionExtensionContext start."); + if (value == nullptr) { + SELECTION_HILOGW("parameter is invalid."); + return nullptr; + } + auto ptr = reinterpret_cast*>(value)->lock(); + if (ptr == nullptr) { + SELECTION_HILOGW("context is invalid."); + return nullptr; + } + napi_value object = CreateJsSelectionExtensionContext(env, ptr); + auto systemModule = JsRuntime::LoadSystemModuleByEngine(env, "selectionInput.SelectionExtensionContext", + &object, 1); + if (systemModule == nullptr) { + SELECTION_HILOGE("failed to load system module by engine!"); + return nullptr; + } + auto contextObj = systemModule->GetNapiValue(); + napi_coerce_to_native_binding_object(env, contextObj, DetachCallbackFunc, AttachSelectionExtensionContext, value, + nullptr); + auto workContext = new (std::nothrow) std::weak_ptr(ptr); + if (workContext == nullptr) { + SELECTION_HILOGE("workContext is nullptr!"); + return nullptr; + } + napi_status status = napi_wrap( + env, contextObj, workContext, + [](napi_env, void* data, void*) { + SELECTION_HILOGI("finalizer for weak_ptr input method extension context is called."); + delete static_cast*>(data); + }, + nullptr, nullptr); + if (status != napi_ok) { + SELECTION_HILOGE("SelectionExtensionContext wrap failed: %{public}d!", status); + delete workContext; + return nullptr; + } + return object; +} + +JsSelectionExtension::JsSelectionExtension(JsRuntime& jsRuntime) : jsRuntime_(jsRuntime) {} +JsSelectionExtension::~JsSelectionExtension() +{ + jsRuntime_.FreeNativeReference(std::move(jsObj_)); +} + +JsSelectionExtension* JsSelectionExtension::Create(const std::unique_ptr& runtime) +{ + return new (std::nothrow) JsSelectionExtension(static_cast(*runtime)); +} + +void JsSelectionExtension::Init(const std::shared_ptr& record, + const std::shared_ptr& application, + std::shared_ptr& handler, + const sptr& token) +{ + SELECTION_HILOGI("%{public}s start.", __func__); + SelectionExtension::Init(record, application, handler, token); + std::string srcPath; + GetSrcPath(srcPath); + if (srcPath.empty()) { + SELECTION_HILOGE("failed to get srcPath!"); + return; + } + + std::string moduleName(Extension::abilityInfo_->moduleName); + moduleName.append("::").append(abilityInfo_->name); + SELECTION_HILOGI("JsSelectionExtension, module: %{public}s, srcPath:%{public}s.", moduleName.c_str(), + srcPath.c_str()); + HandleScope handleScope(jsRuntime_); + jsObj_ = jsRuntime_.LoadModule(moduleName, srcPath, abilityInfo_->hapPath, + abilityInfo_->compileMode == CompileMode::ES_MODULE); + if (jsObj_ == nullptr) { + SELECTION_HILOGE("failed to init jsObj_!"); + return; + } + + napi_env env = jsRuntime_.GetNapiEnv(); + napi_value obj = jsObj_->GetNapiValue(); + if (obj == nullptr) { + SELECTION_HILOGE("failed to get JsSelectionExtension object!"); + return; + } + BindContext(env, obj); + SELECTION_HILOGI("%{public}s end.", __func__); +} + +sptr JsSelectionExtension::OnConnect(const AAFwk::Want& want) +{ + SELECTION_HILOGI("%{public}s start.", __func__); + HandleScope handleScope(jsRuntime_); + Extension::OnConnect(want); + napi_env env = jsRuntime_.GetNapiEnv(); + napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want); + napi_value argv[] = {napiWant}; + napi_value result = CallObjectMethod("onConnect", argv, ARGC_ONE); + auto remoteObj = NAPI_ohos_rpc_getNativeRemoteObject(env, result); + if (remoteObj == nullptr) { + SELECTION_HILOGE("remoteObj is nullptr."); + } + SELECTION_HILOGI("%{public}s end.", __func__); + return remoteObj; +} + +void JsSelectionExtension::OnDisconnect(const AAFwk::Want& want) +{ + SELECTION_HILOGI("%{public}s start.", __func__); + HandleScope handleScope(jsRuntime_); + Extension::OnDisconnect(want); + napi_env env = jsRuntime_.GetNapiEnv(); + napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want); + napi_value argv[] = {napiWant}; + CallObjectMethod("onDisconnect", argv, ARGC_ONE); + SELECTION_HILOGI("%{public}s end.", __func__); +} + +napi_value JsSelectionExtension::CallObjectMethod(const char* methodName, const napi_value* argv, size_t argc) +{ + SELECTION_HILOGI("JsSelectionExtension::CallObjectMethod(%{public}s), start.", methodName); + + if (argc > 0 && argv == nullptr) { + SELECTION_HILOGE("argc > 0 && argv == nullptr."); + return nullptr; + } + + if (jsObj_ == nullptr) { + SELECTION_HILOGE("not found JsSelectionExtension.js."); + return nullptr; + } + + napi_env env = jsRuntime_.GetNapiEnv(); + napi_value obj = jsObj_->GetNapiValue(); + if (obj == nullptr) { + SELECTION_HILOGE("failed to get JsSelectionExtension object!"); + return nullptr; + } + + napi_value method = nullptr; + napi_get_named_property(env, obj, methodName, &method); + if (method == nullptr) { + SELECTION_HILOGE("failed to get '%{public}s' from JsSelectionExtension object!", methodName); + return nullptr; + } + napi_value result = nullptr; + if (napi_call_function(env, obj, method, argc, argv, &result) != napi_ok) { + SELECTION_HILOGE("JsSelectionExtension::CallFunction(%{public}s), failed.", methodName); + return nullptr; + } + SELECTION_HILOGI("JsSelectionExtension::CallFunction(%{public}s), success.", methodName); + return result; +} + +void JsSelectionExtension::GetSrcPath(std::string& srcPath) +{ + if (!Extension::abilityInfo_->isModuleJson) { + std::filesystem::path modulePath(Extension::abilityInfo_->package); + modulePath /= "assets/js"; + + if (!Extension::abilityInfo_->srcPath.empty()) { + modulePath /= Extension::abilityInfo_->srcPath; + } + + modulePath /= (Extension::abilityInfo_->name + ".abc"); + srcPath = modulePath.string(); + return; + } + + if (!Extension::abilityInfo_->srcEntrance.empty()) { + std::filesystem::path modulePath(Extension::abilityInfo_->moduleName); + modulePath /= Extension::abilityInfo_->srcEntrance; + modulePath.replace_extension(".abc"); + srcPath = modulePath.string(); + } +} + +void JsSelectionExtension::BindContext(napi_env env, napi_value obj) +{ + SELECTION_HILOGI("%{public}s start.", __func__); + auto context = GetContext(); + if (context == nullptr) { + SELECTION_HILOGE("failed to get context!"); + return; + } + SELECTION_HILOGD("JsSelectionExtension::Init CreateJsSelectionExtensionContext."); + napi_value contextObj = CreateJsSelectionExtensionContext(env, context); + auto shellContextRef = jsRuntime_.LoadSystemModule("selectionInput.SelectionExtensionContext", + &contextObj, ARGC_ONE); + if (shellContextRef == nullptr) { + SELECTION_HILOGE("shellContextRef is nullptr!"); + return; + } + contextObj = shellContextRef->GetNapiValue(); + if (contextObj == nullptr) { + SELECTION_HILOGE("failed to get input method extension native object!"); + return; + } + auto workContext = new (std::nothrow) std::weak_ptr(context); + if (workContext == nullptr) { + SELECTION_HILOGE("workContext is nullptr!"); + return; + } + napi_status status = napi_coerce_to_native_binding_object(env, contextObj, DetachCallbackFunc, + AttachSelectionExtensionContext, workContext, nullptr); + if (status != napi_ok) { + SELECTION_HILOGE("napi_coerce_to_native_binding_object failed!"); + return; + } + SELECTION_HILOGD("JsSelectionExtension::Init Bind."); + context->Bind(jsRuntime_, shellContextRef.release()); + SELECTION_HILOGD("JsSelectionExtension::SetProperty."); + napi_set_named_property(env, obj, "context", contextObj); + status = napi_wrap( + env, contextObj, workContext, + [](napi_env, void* data, void*) { + SELECTION_HILOGI("Finalizer for weak_ptr input method extension context is called."); + delete static_cast*>(data); + }, + nullptr, nullptr); + if (status != napi_ok) { + SELECTION_HILOGE("SelectionExtensionContext wrap failed: %{public}d", status); + delete workContext; + } + SELECTION_HILOGI("%{public}s end.", __func__); +} + +} // namespace OHOS::AbilityRuntime \ No newline at end of file diff --git a/frameworks/native/selection_extension/src/js_selection_extension_context.cpp b/frameworks/native/selection_extension/src/js_selection_extension_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..875cabed4ce960d90fe151bda6f57fdeecdcf2f1 --- /dev/null +++ b/frameworks/native/selection_extension/src/js_selection_extension_context.cpp @@ -0,0 +1,114 @@ +/* + * 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 "js_selection_extension_context.h" +#include "js_error_utils.h" +#include "js_extension_context.h" +#include "js_runtime_utils.h" +#include "selection_js_utils.h" +#include "napi_common_want.h" +#include "selection_log.h" + +namespace OHOS::AbilityRuntime { +using namespace OHOS::SelectionFwk; + +namespace { +constexpr int32_t INDEX_ZERO = 0; +constexpr int32_t ERROR_CODE_ONE = 1; +constexpr size_t ARGC_ONE = 1; + +class JsSelectionExtensionContext final { +public: + explicit JsSelectionExtensionContext(const std::shared_ptr& context) : context_(context) + { + SELECTION_HILOGI("JsSelectionExtensionContext::JsSelectionExtensionContext is called."); + } + JsSelectionExtensionContext() = default; + ~JsSelectionExtensionContext() = default; + + static void Finalizer(napi_env env, void* data, void* hint) + { + SELECTION_HILOGI("JsSelectionExtensionContext::Finalizer is called."); + std::unique_ptr(static_cast(data)); + } + + static napi_value StartAbility(napi_env env, napi_callback_info info) + { + GET_CB_INFO_AND_CALL(env, info, JsSelectionExtensionContext, OnStartAbility); + } + +private: + std::weak_ptr context_; + + napi_value OnStartAbility(napi_env env, size_t argc, napi_value* argv) + { + SELECTION_HILOGI("SelectionExtensionContext OnStartAbility."); + // only support one params + PARAM_CHECK_RETURN(env, argc == ARGC_ONE, "number of param should in 1", TYPE_NONE, CreateJsUndefined(env)); + PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "param want type must be Want", TYPE_NONE, + JsUtil::Const::Null(env)); + decltype(argc) unwrapArgc = 0; + AAFwk::Want want; + OHOS::AppExecFwk::UnwrapWant(env, argv[INDEX_ZERO], want); + SELECTION_HILOGI("%{public}s bundleName: %{public}s abilityName: %{public}s.", __func__, + want.GetBundle().c_str(), want.GetElement().GetAbilityName().c_str()); + unwrapArgc++; + napi_value lastParam = argc > unwrapArgc ? argv[unwrapArgc] : nullptr; + napi_value result = nullptr; + std::unique_ptr napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result); + auto asyncTask = [weak = context_, want, unwrapArgc, env, task = napiAsyncTask.get()]() { + SELECTION_HILOGI("startAbility start."); + auto context = weak.lock(); + if (context == nullptr) { + SELECTION_HILOGW("context is released."); + task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released")); + delete task; + return; + } + ErrCode errcode = (unwrapArgc == 1) ? context->StartAbility(want) : ERR_INVALID_VALUE; + if (errcode == 0) { + task->Resolve(env, CreateJsUndefined(env)); + } else { + task->Reject(env, CreateJsErrorByNativeErr(env, errcode)); + } + delete task; + }; + if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) { + napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed")); + } else { + napiAsyncTask.release(); + } + return result; + } +}; +} // namespace + +napi_value CreateJsSelectionExtensionContext(napi_env env, std::shared_ptr context) +{ + SELECTION_HILOGI("CreateJsSelectionExtensionContext begin"); + std::shared_ptr abilityInfo = nullptr; + if (context) { + abilityInfo = context->GetAbilityInfo(); + } + + napi_value objValue = CreateJsExtensionContext(env, context, abilityInfo); + std::unique_ptr jsContext = std::make_unique(context); + napi_wrap(env, objValue, jsContext.release(), JsSelectionExtensionContext::Finalizer, nullptr, nullptr); + + const char* moduleName = "JsSelectionExtensionContext"; + BindNativeFunction(env, objValue, "startAbility", moduleName, JsSelectionExtensionContext::StartAbility); + return objValue; +} +} // namespace OHOS::AbilityRuntime diff --git a/frameworks/native/selection_extension/src/selection_extension.cpp b/frameworks/native/selection_extension/src/selection_extension.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef5991e7d62899e59a7a9a0cf01d26aad6d1e24b --- /dev/null +++ b/frameworks/native/selection_extension/src/selection_extension.cpp @@ -0,0 +1,47 @@ +/* + * 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 "selection_extension.h" +#include "js_selection_extension.h" +#include "runtime.h" +#include "selection_log.h" + +namespace OHOS::AbilityRuntime { +using namespace OHOS::AppExecFwk; + +SelectionExtension* SelectionExtension::Create(const std::unique_ptr& runtime) +{ + if (runtime == nullptr) { + return new SelectionExtension(); + } + switch (runtime->GetLanguage()) { + case Runtime::Language::JS: + SELECTION_HILOGI("create JsSelectionExtension"); + return JsSelectionExtension::Create(runtime); + default: + return new SelectionExtension(); + } +} + +void SelectionExtension::Init(const std::shared_ptr& record, + const std::shared_ptr& application, + std::shared_ptr& handler, + const sptr& token) +{ + SELECTION_HILOGI("call %{public}s", __func__); + ExtensionBase::Init(record, application, handler, token); +} + +} // namespace OHOS::AbilityRuntime \ No newline at end of file diff --git a/frameworks/native/selection_extension/src/selection_extension_context.cpp b/frameworks/native/selection_extension/src/selection_extension_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d0163c701a52d1532f3aff47f5f02840fe467970 --- /dev/null +++ b/frameworks/native/selection_extension/src/selection_extension_context.cpp @@ -0,0 +1,35 @@ +/* + * 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 "selection_extension_context.h" +#include "ability_manager_client.h" +#include "selection_log.h" + +namespace OHOS::AbilityRuntime { +const size_t SelectionExtensionContext::CONTEXT_TYPE_ID(std::hash {}("SelectionExtensionContext")); +int32_t SelectionExtensionContext::ILLEGAL_REQUEST_CODE(-1); + +ErrCode SelectionExtensionContext::StartAbility(const AAFwk::Want& want) const +{ + SELECTION_HILOGD("%{public}s begin.", __func__); + ErrCode err = AAFwk::AbilityManagerClient::GetInstance()->StartAbility(want, token_, ILLEGAL_REQUEST_CODE); + SELECTION_HILOGD("%{public}s ret=%{public}d", __func__, err); + if (err != ERR_OK) { + SELECTION_HILOGE("SelectionExtensionContext::StartAbility failed: %{public}d", err); + } + return err; +} + +} // namespace OHOS::AbilityRuntime \ No newline at end of file diff --git a/frameworks/native/selection_extension/src/selection_extension_module_loader.cpp b/frameworks/native/selection_extension/src/selection_extension_module_loader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4c24a86b34be84b202bfd89f34e3639795d8d6d --- /dev/null +++ b/frameworks/native/selection_extension/src/selection_extension_module_loader.cpp @@ -0,0 +1,39 @@ +/* + * 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 "selection_extension_module_loader.h" +#include "selection_extension.h" + +namespace OHOS::AbilityRuntime { + +SelectionExtensionModuleLoader::SelectionExtensionModuleLoader() = default; +SelectionExtensionModuleLoader::~SelectionExtensionModuleLoader() = default; + +Extension* SelectionExtensionModuleLoader::Create(const std::unique_ptr& runtime) const +{ + return SelectionExtension::Create(runtime); +} + +std::map SelectionExtensionModuleLoader::GetParams() +{ + // type means extension type in ExtensionAbilityType of extension_ability_info.h + return {{"type", "31"}, {"name", "SelectionExtensionAbility"}}; +} + +extern "C" __attribute__((visibility("default"))) void* OHOS_EXTENSION_GetExtensionModule() +{ + return &SelectionExtensionModuleLoader::GetInstance(); +} +} // namespace OHOS::AbilityRuntime \ No newline at end of file diff --git a/hiappevent_agent/BUILD.gn b/hiappevent_agent/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..de9c59968884c7430256b0446bb9c6d8763a2cd2 --- /dev/null +++ b/hiappevent_agent/BUILD.gn @@ -0,0 +1,44 @@ +# 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("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") +import("//build/ohos.gni") + +ohos_shared_library("selection_hiappevent_agent") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + integer_overflow = true + ubsan = true + } + + include_dirs = [ + ".", + "../utils/include", + ] + + sources = [ "selection_api_event_reporter.cpp" ] + + external_deps = [ + "hilog:libhilog", + "hiappevent:hiappevent_innerapi", + "hitrace:hitrace_meter", + "hitrace:libhitracechain", + ] + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} diff --git a/hiappevent_agent/selection_api_event_reporter.cpp b/hiappevent_agent/selection_api_event_reporter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..627574242660a4431937bcf030b20dede8358161 --- /dev/null +++ b/hiappevent_agent/selection_api_event_reporter.cpp @@ -0,0 +1,71 @@ +/* + * 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 "selection_api_event_reporter.h" +#include +#include "app_event.h" +#include "app_event_processor_mgr.h" +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { +static int64_t g_processId = -1; +static std::mutex g_mutex; +static const std::string SDK_NAME("BasicServicesKit"); + +RandomGenerator SelectionApiEventReporter::randomGenerator_; + +SelectionApiEventReporter::SelectionApiEventReporter(const std::string& apiName) : apiName_(apiName) +{ + SELECTION_HILOGI("Create SelectionApiEventReporter object with apiName: %{public}s", apiName.c_str()); + transId_ = GenerateTransId(); + beginTime_ = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + std::unique_lock lock(g_mutex); + if (g_processId == -1) { + HiviewDFX::HiAppEvent::ReportConfig config; + config.name = "ha_app_event"; + config.configName = "SDK_OCG"; + g_processId = HiviewDFX::HiAppEvent::AppEventProcessorMgr::AddProcessor(config); + } +} + +void SelectionApiEventReporter::WriteEndEvent(const int result, const int32_t errCode) +{ + SELECTION_HILOGI("Begin to WriteEndEvent with result: %{public}d, errCode: %{public}d",result, errCode); + int64_t endTime = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + HiviewDFX::HiAppEvent::Event event("api_diagnostic", "api_exec_end", HiviewDFX::HiAppEvent::BEHAVIOR); + event.AddParam("trans_id", transId_); + event.AddParam("api_name", apiName_); + event.AddParam("sdk_name", SDK_NAME); + event.AddParam("begin_time", beginTime_); + event.AddParam("end_time", endTime); + event.AddParam("result", result); + event.AddParam("error_code", errCode); + int32_t ret = Write(event); + SELECTION_HILOGI("WriteEndEvent transId:%{public}s, apiName:%{public}s, sdkName:%{public}s, result:%{public}d, " + "errCode:%{public}d, ret:%{public}d", + transId_.c_str(), apiName_.c_str(), SDK_NAME.c_str(), result, errCode, ret); + if (ret != 0) { + SELECTION_HILOGE("WriteEndEvent HiAppEvent failed, ret: %{public}d", ret); + } +} + +std::string SelectionApiEventReporter::GenerateTransId() const +{ + return std::string("transId_") + std::to_string(randomGenerator_.Generate()); +} +} // SelectionFwk +} // OHOS \ No newline at end of file diff --git a/hiappevent_agent/selection_api_event_reporter.h b/hiappevent_agent/selection_api_event_reporter.h new file mode 100644 index 0000000000000000000000000000000000000000..014cc66e0a8604514136b3bbb503643aff58c446 --- /dev/null +++ b/hiappevent_agent/selection_api_event_reporter.h @@ -0,0 +1,65 @@ +/* + * 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 SELECTION_API_EVENT_REPORTER_H +#define SELECTION_API_EVENT_REPORTER_H + +#include +#include +#include +#include + +namespace OHOS { +namespace SelectionFwk { +class RandomGenerator { +public: + RandomGenerator() { + engine_ = std::make_unique(rd_()); + } + + int Generate() { + std::uniform_int_distribution dist; + std::lock_guard lock(mtx_); + return dist(*engine_); + } + +private: + std::random_device rd_; + std::unique_ptr engine_; + std::mutex mtx_; +}; + +class SelectionApiEventReporter { +public: + explicit SelectionApiEventReporter(const std::string& apiName); + ~SelectionApiEventReporter() = default; + void WriteEndEvent(const int result, const int32_t errCode); + +public: + constexpr static int32_t API_SUCCESS = 0; + constexpr static int32_t API_FAIL = 1; + +private: + std::string GenerateTransId() const; + +private: + static RandomGenerator randomGenerator_; + + std::string transId_; + std::string apiName_; + int64_t beginTime_; +}; +} // SelectionFwk +} // OHOS +#endif // SELECTION_API_EVENT_REPORTER_H diff --git a/interfaces/idl/BUILD.gn b/interfaces/idl/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..eca108fb25edb5c53c0769946dc3a50a560db25c --- /dev/null +++ b/interfaces/idl/BUILD.gn @@ -0,0 +1,136 @@ +# 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/ohos.gni") +import("//build/config/components/idl_tool/idl.gni") +import("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +idl_gen_interface("selection_listener_interface") { + src_idl = rebase_path("ISelectionListener.idl") +} + +idl_gen_interface("selection_service_interface") { + src_idl = rebase_path("ISelectionService.idl") +} + +config("selection_listener_config") { + include_dirs = [ + "include", + "${selection_fwk_root_path}/common", + "${target_gen_dir}", + ] +} + +config("selection_service_config") { + include_dirs = [ + "include", + "${selection_fwk_root_path}/common", + "${target_gen_dir}", + ] +} + +ohos_source_set("selection_listener_proxy") { + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + include_dirs = [ + "${target_gen_dir}", + ] + + public_configs = [":selection_listener_config"] + output_values = get_target_outputs(":selection_listener_interface") + sources = filter_include(output_values, [ "*_proxy.cpp" ]) + deps = [ ":selection_listener_interface" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_single", + ] + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} + +ohos_source_set("selection_listener_stub") { + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + include_dirs = [ + "${target_gen_dir}", + ] + + public_configs = [":selection_listener_config"] + output_values = get_target_outputs(":selection_listener_interface") + sources = filter_include(output_values, [ "*_stub.cpp" ]) + deps = [ ":selection_listener_interface" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_single", + ] + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} + +ohos_source_set("selection_service_proxy") { + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + include_dirs = [ + "${target_gen_dir}", + ] + + public_configs = [":selection_service_config"] + output_values = get_target_outputs(":selection_service_interface") + sources = filter_include(output_values, [ "*_proxy.cpp" ]) + deps = [ ":selection_service_interface" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_single", + ] + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} + +ohos_source_set("selection_service_stub") { + sanitize = { + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + } + include_dirs = [ + "${target_gen_dir}", + ] + + public_configs = [":selection_service_config"] + output_values = get_target_outputs(":selection_service_interface") + sources = filter_include(output_values, [ "*_stub.cpp" ]) + deps = [ ":selection_service_interface" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_single", + ] + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} diff --git a/interfaces/idl/ISelectionListener.idl b/interfaces/idl/ISelectionListener.idl new file mode 100644 index 0000000000000000000000000000000000000000..febdb021cffa6012b7f24723857fb5f8c367cfa4 --- /dev/null +++ b/interfaces/idl/ISelectionListener.idl @@ -0,0 +1,22 @@ +/* + * 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. + */ + +sequenceable selection_data_inner..OHOS.SelectionFwk.SelectionInfoData; +sequenceable selection_data_inner..OHOS.SelectionFwk.SelectionFocusChangeInfo; + +interface OHOS.SelectionFwk.ISelectionListener { + void OnSelectionChange([in] SelectionInfoData selectionInfoData); + void FocusChange([in] SelectionFocusChangeInfo focusChangeInfo); +} diff --git a/interfaces/idl/ISelectionService.idl b/interfaces/idl/ISelectionService.idl new file mode 100644 index 0000000000000000000000000000000000000000..c64f3b2b34b068e724c892b4b98c3248ce125c58 --- /dev/null +++ b/interfaces/idl/ISelectionService.idl @@ -0,0 +1,22 @@ +/* + * 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. + */ + +interface OHOS.SelectionFwk.ISelectionListener; + +interface OHOS.SelectionFwk.ISelectionService { + void RegisterListener([in] ISelectionListener listener); + void UnregisterListener([in] ISelectionListener listener); + void IsCurrentSelectionApp([in] int pid, [out] boolean resultValue); +} \ No newline at end of file diff --git a/interfaces/inner_kits/selection_client/BUILD.gn b/interfaces/inner_kits/selection_client/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..21713c0ba677588434e196ba952dad3d5b2a83dd --- /dev/null +++ b/interfaces/inner_kits/selection_client/BUILD.gn @@ -0,0 +1,75 @@ +# 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/ohos.gni") +import("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + + +config("selection_client_native_public_config") { + visibility = [ + "./*", + "//foundation/systemabilitymgr/selectionfwk/interfaces/inner_kits/selection_client/*", + ] + include_dirs = [ + "include", + ] +} + +ohos_shared_library("selection_client") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + integer_overflow = true + ubsan = true + } + + version_script = "selection_client.versionscript" + + innerapi_tags = [ "platformsdk" ] + + include_dirs = [ + "include", + "../../../utils/include", + ] + + sources = [ + "../../../frameworks/native/selection_client/selection_client.cpp", + ] + + deps = [ + "${selection_fwk_root_path}/interfaces/idl:selection_service_proxy", + "${selection_fwk_root_path}/interfaces/idl:selection_listener_proxy", + ] + + cflags_cc = [ "-std=c++17" ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_core", + "ipc:ipc_single", + "ffrt:libffrt", + "samgr:samgr_proxy", + ] + + public_configs = [ + ":selection_client_native_public_config", + ] + + subsystem_name = "systemabilitymgr" + part_name = "selectionfwk" +} diff --git a/interfaces/inner_kits/selection_client/include/selection_client.h b/interfaces/inner_kits/selection_client/include/selection_client.h new file mode 100644 index 0000000000000000000000000000000000000000..f249bb408a90c9c087139c806d925afac82564ea --- /dev/null +++ b/interfaces/inner_kits/selection_client/include/selection_client.h @@ -0,0 +1,27 @@ +/* + * 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 SELECTION_CLIENT_H +#define SELECTION_CLIENT_H + +#include "visibility.h" + +class SelectionClient { +public: + SELECTION_API static SelectionClient& GetInstance(); + bool IsCurrentSelectionApp(int pid); +}; + +#endif // SELECTION_CLIENT_H \ No newline at end of file diff --git a/interfaces/inner_kits/selection_client/include/visibility.h b/interfaces/inner_kits/selection_client/include/visibility.h new file mode 100644 index 0000000000000000000000000000000000000000..f50aad35bba18c286b47763de08662ebfba29b84 --- /dev/null +++ b/interfaces/inner_kits/selection_client/include/visibility.h @@ -0,0 +1,23 @@ +/* + * 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 OH_SELECTION_VISIBILITY_H +#define OH_SELECTION_VISIBILITY_H + +#ifndef SELECTION_API +#define SELECTION_API __attribute__((visibility("default"))) +#endif + +#endif // OH_SELECTION_VISIBILITY_H diff --git a/interfaces/inner_kits/selection_client/selection_client.versionscript b/interfaces/inner_kits/selection_client/selection_client.versionscript new file mode 100644 index 0000000000000000000000000000000000000000..217b0dc08a2f7d7a7983c34c82d1c8fbd6f11fef --- /dev/null +++ b/interfaces/inner_kits/selection_client/selection_client.versionscript @@ -0,0 +1,19 @@ +# 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. + +1.0 { + global: + *SelectionClient*; + local: + *; +}; diff --git a/patches/patches.json b/patches/patches.json new file mode 100644 index 0000000000000000000000000000000000000000..140a4c827978fa3c2dea7cf2bcb9777d80d641cd --- /dev/null +++ b/patches/patches.json @@ -0,0 +1,9 @@ +{ + "patches": [ + { + "project":"productdefine_common", + "path":"productdefine/common", + "pr_url":"https://gitee.com/openharmony/productdefine_common/pulls/1032" + } + ] +} \ No newline at end of file diff --git a/sa_profile/8500.json b/sa_profile/8500.json new file mode 100644 index 0000000000000000000000000000000000000000..cbb7f40d77437c201c86da3229044b65dbda1478 --- /dev/null +++ b/sa_profile/8500.json @@ -0,0 +1,32 @@ +{ + "process": "selection_service", + "systemability": [ + { + "name": 8500, + "libpath": "libselection_service.z.so", + "run-on-create": false, + "distributed": false, + "bootphase": "BootStartPhase", + "dump_level": 1, + "auto-restart" : true, + "start-on-demand": { + "allow-update": true, + "param": [ + { + "name":"sys.selection.switch", + "value":"on" + } + ] + }, + "stop-on-demand": { + "allow-update": true, + "param": [ + { + "name":"sys.selection.switch", + "value":"off" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/sa_profile/BUILD.gn b/sa_profile/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..87a3f4eee58673668e3d00120a9d5a9a37e7d427 --- /dev/null +++ b/sa_profile/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright (c) 2021-2022 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/ohos/sa_profile/sa_profile.gni") +import("../selection_service.gni") + +ohos_sa_profile("selection_service_sa_profile") { + sources = [ "8500.json" ] + + part_name = "selectionfwk" +} \ No newline at end of file diff --git a/selection_service.gni b/selection_service.gni new file mode 100644 index 0000000000000000000000000000000000000000..54394ddee3ca4ce363dc524c6b5a0ebdde1f3e6e --- /dev/null +++ b/selection_service.gni @@ -0,0 +1,32 @@ +# Copyright (c) 2021 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/ohos.gni") + +declare_args() { + word_selection_feature_test_one = true +} + + +ability_runtime_path = "//foundation/ability/ability_runtime" + +ability_runtime_inner_api_path = "${ability_runtime_path}/interfaces/inner_api" + +ability_runtime_services_path = "${ability_runtime_path}/services" +word_selection_part_name = "word_selection" + +selection_fwk_root_path = "//foundation/systemabilitymgr/selectionfwk" + +selection_path = "//commonlibrary/c_utils" + +system_type = "default" \ No newline at end of file diff --git a/service/BUILD.gn b/service/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..b830460b9b792847f26b965e1073d1e63b1be487 --- /dev/null +++ b/service/BUILD.gn @@ -0,0 +1,98 @@ +# 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/ohos.gni") +import("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +config("selection_sa_config") { + include_dirs = [ + "include", + "../utils/include", + "${target_gen_dir}", + ] +} + +ohos_shared_library("selection_service") { + branch_protector_ret = "pac_ret" + sanitize = { + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + cfi_vcall_icall_only = true + debug = false + integer_overflow = true + ubsan = true + } + cflags_cc = [ + "-fdata-sections", + "-ffunction-sections", + "-Oz", + "-std=c++17", + ] + configs = [ ":selection_sa_config", ] + + include_dirs = [ + "./focus_monitor/include", + ] + + sources = [ + "src/db_selection_config_repository.cpp", + "src/selection_service.cpp", + "src/selection_input_monitor.cpp", + "focus_monitor/src/focus_change_listener.cpp", + "focus_monitor/src/focus_monitor_manager.cpp", + "src/selection_app_validator.cpp", + "src/selection_config.cpp", + "src/selection_config_database.cpp", + "src/sys_selection_config_repository.cpp", + "src/selection_config_comparator.cpp", + "src/selection_common.cpp", + ] + deps = [ + "${selection_fwk_root_path}/interfaces/idl:selection_service_interface", + "${selection_fwk_root_path}/common:selection_common", + "${selection_fwk_root_path}/interfaces/idl:selection_service_stub", + "${selection_fwk_root_path}/interfaces/idl:selection_listener_proxy", + ] + + external_deps = [ + "ability_base:want", + "ability_runtime:ability_manager", + "access_token:libaccesstoken_sdk", + "c_utils:utils", + "napi:ace_napi", + "hilog:libhilog", + "ipc:ipc_single", + "init:libbeget_proxy", + "input:libmmi-client", + "pasteboard:pasteboard_client", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + "input:libmmi-client", + "window_manager:libdm_lite", + "window_manager:libwsutils", + "os_account:os_account_innerkits", + "relational_store:native_rdb", + ] + + defines = [] + if (window_manager_use_sceneboard) { + external_deps += [ "window_manager:libwm_lite" ] + defines += [ "SCENE_BOARD_ENABLE" ] + } else { + external_deps += [ "window_manager:libwm" ] + } + + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} \ No newline at end of file diff --git a/service/ISelectionService.idl b/service/ISelectionService.idl new file mode 100644 index 0000000000000000000000000000000000000000..fef8523a3e06b8c4981c7f826a269c492d61cab5 --- /dev/null +++ b/service/ISelectionService.idl @@ -0,0 +1,20 @@ +/* + * 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. + */ + +sequenceable OHOS.IRemoteObject; +interface OHOS.SelectionFwk.ISelectionService { + void RegisterListener([in] IRemoteObject listener); + void UnregisterListener([in] IRemoteObject listener); +} \ No newline at end of file diff --git a/service/focus_monitor/include/focus_change_listener.h b/service/focus_monitor/include/focus_change_listener.h new file mode 100644 index 0000000000000000000000000000000000000000..ec86714eec08fa5aa480626a37c5fc55b42246aa --- /dev/null +++ b/service/focus_monitor/include/focus_change_listener.h @@ -0,0 +1,41 @@ +/* + * 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 SELECTION_FOCUS_CHANGE_LISTENER_H +#define SELECTION_FOCUS_CHANGE_LISTENER_H + +#include "focus_monitor_manager.h" +#ifdef SCENE_BOARD_ENABLE +#include "window_manager_lite.h" +#else +#include "window_manager.h" +#endif + +namespace OHOS { +namespace SelectionFwk { +class FocusChangedListener : public Rosen::IFocusChangedListener { +public: + explicit FocusChangedListener(const FocusHandle &handle) : focusHandle_(handle){}; + ~FocusChangedListener() = default; + void OnFocused(const sptr &focusChangeInfo) override; + void OnUnfocused(const sptr &focusChangeInfo) override; + +private: + FocusHandle focusHandle_ = nullptr; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // SELECTION_FOCUS_CHANGE_LISTENER_H diff --git a/service/focus_monitor/include/focus_monitor_manager.h b/service/focus_monitor/include/focus_monitor_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..18d813e42ae209a38bd5c627ef0af746fe307924 --- /dev/null +++ b/service/focus_monitor/include/focus_monitor_manager.h @@ -0,0 +1,44 @@ +/* + * 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 SELECTION_FOCUS_MONITOR_MANAGER_H +#define SELECTION_FOCUS_MONITOR_MANAGER_H + +#include +#include "focus_change_info.h" +#ifdef SCENE_BOARD_ENABLE +#include "window_manager_lite.h" +#else +#include "window_manager.h" +#endif + +namespace OHOS { +namespace SelectionFwk { +using FocusHandle = std::function &focusChangeInfo, bool)>; +class FocusMonitorManager { +public: + static FocusMonitorManager &GetInstance(); + void RegisterFocusChangedListener(const FocusHandle &handle); + void UnregisterFocusChangedListener(); + +private: + FocusMonitorManager() = default; +private: + sptr focusListener_ = nullptr; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // SELECTION_FOCUS_MONITOR_MANAGER_H diff --git a/service/focus_monitor/src/focus_change_listener.cpp b/service/focus_monitor/src/focus_change_listener.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a365f9d87408b9e2e34d69927fc0207b9741ffa3 --- /dev/null +++ b/service/focus_monitor/src/focus_change_listener.cpp @@ -0,0 +1,46 @@ +/* + * 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 "focus_change_listener.h" + +#include + +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { +void FocusChangedListener::OnFocused(const sptr &focusChangeInfo) +{ + if (focusChangeInfo == nullptr || focusHandle_ == nullptr) { + SELECTION_HILOGE("error nullptr"); + return; + } + SELECTION_HILOGI("windowId:%{public}d displayId: %{public}" PRIu64, + focusChangeInfo->windowId_, focusChangeInfo->displayId_); + focusHandle_(focusChangeInfo, true); +} + +void FocusChangedListener::OnUnfocused(const sptr &focusChangeInfo) +{ + if (focusChangeInfo == nullptr || focusHandle_ == nullptr) { + SELECTION_HILOGE("error nullptr"); + return; + } + SELECTION_HILOGI("windowId:%{public}d displayId: %{public}" PRIu64 " Windowtype: %{public}d", + focusChangeInfo->windowId_, focusChangeInfo->displayId_, focusChangeInfo->windowType_); + focusHandle_(focusChangeInfo, false); +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/service/focus_monitor/src/focus_monitor_manager.cpp b/service/focus_monitor/src/focus_monitor_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce3f149a70a7f2a6bbc6b7b7ae245cc0e9fa1fcf --- /dev/null +++ b/service/focus_monitor/src/focus_monitor_manager.cpp @@ -0,0 +1,63 @@ +/* + * 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 "focus_change_listener.h" +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { +using namespace Rosen; +FocusMonitorManager &FocusMonitorManager::GetInstance() +{ + static FocusMonitorManager focusMonitorManager; + return focusMonitorManager; +} + +void FocusMonitorManager::RegisterFocusChangedListener(const FocusHandle &handle) +{ + if (focusListener_ != nullptr) { + SELECTION_HILOGE("focusListener_ has been registered by others."); + return; + } + + focusListener_ = sptr::MakeSptr(handle); + if (focusListener_ == nullptr) { + SELECTION_HILOGE("failed to create focusListener_"); + return; + } +#ifdef SCENE_BOARD_ENABLE + WMError ret = WindowManagerLite::GetInstance().RegisterFocusChangedListener(focusListener_); +#else + WMError ret = WindowManager::GetInstance().RegisterFocusChangedListener(focusListener_); +#endif + SELECTION_HILOGI("register focus changed focusListener_ ret: %{public}d", ret); +} + +void FocusMonitorManager::UnregisterFocusChangedListener() +{ + if (focusListener_ == nullptr) { + SELECTION_HILOGE("focusListener_ is nullptr"); + return; + } +#ifdef SCENE_BOARD_ENABLE + WMError ret = WindowManagerLite::GetInstance().UnregisterFocusChangedListener(focusListener_); +#else + WMError ret = WindowManager::GetInstance().UnregisterFocusChangedListener(focusListener_); +#endif + SELECTION_HILOGI("Unregister focus changed focusListener_ ret: %{public}d", ret); + focusListener_ = nullptr; +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/service/include/PasteboardDisposableObserver.h b/service/include/PasteboardDisposableObserver.h new file mode 100644 index 0000000000000000000000000000000000000000..f2788ec8acac895d37a20ff29086b5ba55732438 --- /dev/null +++ b/service/include/PasteboardDisposableObserver.h @@ -0,0 +1,29 @@ +/* + * 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 PASTEBOARD_DISPOSABLE_OBSERVER_H +#define PASTEBOARD_DISPOSABLE_OBSERVER_H + +#include + +namespace OHOS { +namespace MiscServices { +class PasteboardDisposableObserver { +public: + virtual void OnTextReceived(const std::string &text, int32_t errCode) = 0; +}; +} // namespace MiscServices +} // namespace OHOS +#endif // PASTEBOARD_DISPOSABLE_OBSERVER_H \ No newline at end of file diff --git a/service/include/db_selection_config_repository.h b/service/include/db_selection_config_repository.h new file mode 100644 index 0000000000000000000000000000000000000000..d2a0a81768ce046f2ae14f40513b58a412818769 --- /dev/null +++ b/service/include/db_selection_config_repository.h @@ -0,0 +1,72 @@ +/* + * 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 DB_SELECTION_CONFIG_REPOSITORY_H +#define DB_SELECTION_CONFIG_REPOSITORY_H + +#include +#include +#include + +#include "data_ability_predicates.h" +#include "rdb_errno.h" +#include "rdb_helper.h" +#include "rdb_open_callback.h" +#include "rdb_predicates.h" +#include "rdb_store.h" +#include "result_set.h" +#include "selection_config.h" +#include "selection_config_database.h" +#include "value_object.h" + +namespace OHOS { +namespace SelectionFwk { + +class DbSelectionConfigRepository { +public: + static std::shared_ptr GetInstance(); + int Save(int uid, const SelectionConfig &info); + std::optional GetOneByUserId(int uid); + +private: + struct SelectionConfigTableInfo { + int32_t rowCount; + int32_t columnCount; + int32_t primaryKeyIndex; + int32_t uidIndex; + int32_t enableIndex; + int32_t applicationInfoIndex; + int32_t triggerIndex; + int32_t shortcutKeysIndex; + }; + +private: + DbSelectionConfigRepository(); + DISALLOW_COPY_AND_MOVE(DbSelectionConfigRepository); + int GetConfigFromDatabase(const OHOS::NativeRdb::RdbPredicates &rdbPredicates, + const std::vector &columns, SelectionConfig &info); + int ProcessQueryResult(const std::shared_ptr &resultSet, + SelectionConfig &info); + int RetrieveResultSetMetadata(const std::shared_ptr &resultSet, + struct SelectionConfigTableInfo &table); + + static std::shared_ptr instance_; + std::mutex databaseMutex_; + std::shared_ptr selectionDatabase_; +}; +} // namespace SelectionFwk +} // namespace OHOS + +#endif // DB_SELECTION_CONFIG_REPOSITORY_H \ No newline at end of file diff --git a/service/include/selection_app_validator.h b/service/include/selection_app_validator.h new file mode 100644 index 0000000000000000000000000000000000000000..e28848f207e4f6414a7f683f6a56c8046e2e4c23 --- /dev/null +++ b/service/include/selection_app_validator.h @@ -0,0 +1,41 @@ +/* + * 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 SELECTION_APP_VALIDATOR_H +#define SELECTION_APP_VALIDATOR_H + +#include +#include +#include + +namespace OHOS::SelectionFwk { +class SelectionAppValidator { +public: + static SelectionAppValidator& GetInstance(); + bool Validate() const; + +private: + SelectionAppValidator() = default; + SelectionAppValidator(const SelectionAppValidator&) = delete; + SelectionAppValidator(SelectionAppValidator&&) = delete; + SelectionAppValidator& operator= (const SelectionAppValidator&) = delete; + SelectionAppValidator& operator= (SelectionAppValidator&&) = delete; + + std::optional GetCurrentBundleName() const; + std::optional GetBundleNameFromSys() const; +}; + +} // namespace OHOS::SelectionFwk +#endif // SELECTION_APP_VALIDATOR_H \ No newline at end of file diff --git a/service/include/selection_common.h b/service/include/selection_common.h new file mode 100644 index 0000000000000000000000000000000000000000..82e65e23e1558d6b229f7b2d1e8ce37f977542ae --- /dev/null +++ b/service/include/selection_common.h @@ -0,0 +1,21 @@ +/* + * 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 SELECTION_COMMON_H +#define SELECTION_COMMON_H + +bool isNumber(const std::string& str); + +#endif // SELECTION_COMMON_H \ No newline at end of file diff --git a/service/include/selection_config.h b/service/include/selection_config.h new file mode 100644 index 0000000000000000000000000000000000000000..a092fc85bda78c3990f3f6b0ae28129ffad3cc31 --- /dev/null +++ b/service/include/selection_config.h @@ -0,0 +1,65 @@ +/* + * 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 SELECTION_CONFIG_H +#define SELECTION_CONFIG_H + +#include +#include + +namespace OHOS { +namespace SelectionFwk { +class SelectionConfig { +public: + bool GetEnable() const; + bool GetTriggered() const; + int GetUid() const; + std::string GetApplicationInfo() const; + void SetEnabled(bool enabled); + void SetTriggered(bool isTriggered); + void SetApplicationInfo(const std::string &applicationInfo); + void SetUid(int uid); + + std::string ToString() const; + +private: + bool isEnabled_ = true; + bool isTriggered_ = false; + int uid_ = -1; + std::string applicationInfo_ = "com.huawei.hmos.vassistant/MiniMenuServiceExtAbility"; +}; + + +class MemSelectionConfig { +public: + static MemSelectionConfig &GetInstance(); + SelectionConfig &GetSelectionConfig(); + void SetSelectionConfig(const SelectionConfig &config); + bool GetEnable() const; + bool GetTriggered() const; + std::string GetApplicationInfo() const; + void SetEnabled(bool enabled); + void SetTriggered(bool isTriggered); + void SetApplicationInfo(const std::string &applicationInfo); + +private: + MemSelectionConfig() = default; + ~MemSelectionConfig() = default; + SelectionConfig delegate_; + mutable std::mutex mutex_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // SELECTION_CONFIG_H \ No newline at end of file diff --git a/service/include/selection_config_comparator.h b/service/include/selection_config_comparator.h new file mode 100644 index 0000000000000000000000000000000000000000..02f90792d14fe63391ad6cc1a774605f8ef7b42a --- /dev/null +++ b/service/include/selection_config_comparator.h @@ -0,0 +1,49 @@ +/* + * 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 SELECTION_CONFIG_COMPARATOR_CPP +#define SELECTION_CONFIG_COMPARATOR_CPP + +#include +#include "selection_config.h" + +namespace OHOS { +namespace SelectionFwk { +enum SyncDirection { + FromDbToSys, + FromSysToDb +}; + +struct ComparisionResult { + SyncDirection direction = FromDbToSys; + bool shouldStop = false; + bool shouldCreate = false; + SelectionConfig selectionConfig; + + std::string ToString() const; +}; + +class SelectionConfigComparator { +public: + static ComparisionResult Compare(int uid, const SelectionConfig &sysSelectionConfig, + std::optional &dbSelectionConfig); + +private: + static ComparisionResult DoCompare(int uid, const SelectionConfig &sysSelectionConfig, + std::optional &dbSelectionConfig); +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // SELECTION_CONFIG_COMPARATOR_CPP \ No newline at end of file diff --git a/service/include/selection_config_database.h b/service/include/selection_config_database.h new file mode 100644 index 0000000000000000000000000000000000000000..3d8125e78053f4450c3ee98ae0179475c0403a44 --- /dev/null +++ b/service/include/selection_config_database.h @@ -0,0 +1,87 @@ +/* + * 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 SELECTION_CONFIG_DATABASE_H +#define SELECTION_CONFIG_DATABASE_H + +#include + +#include "data_ability_predicates.h" +#include "rdb_errno.h" +#include "rdb_helper.h" +#include "rdb_open_callback.h" +#include "rdb_predicates.h" +#include "rdb_store.h" +#include "result_set.h" +#include "value_object.h" + +namespace OHOS { +namespace SelectionFwk { +static std::string SELECTION_CONFIG_DB_PATH = "/data/service/el1/public/selection_service/"; + +constexpr const char *SELECTION_CONFIG_DB_NAME = "selection_config.db"; +constexpr const char *SELECTION_CONFIG_TABLE_NAME = "selection_config"; +constexpr int32_t DATABASE_OPEN_VERSION = 2; + +constexpr const char *CREATE_SELECTION_CONFIG_TABLE = "CREATE TABLE IF NOT EXISTS [selection_config](" + "[id] INTEGER PRIMARY KEY AUTOINCREMENT, " + "[enable] INTEGER, " + "[bundleName] TEXT, " + "[trigger] INTEGER, " + "[shortcutKeys] TEXT, " + "[uid] TEXT NOT NULL UNIQUE);"; +constexpr const char *SQL_ADD_TOKEN_ID = "ALTER TABLE selection_config ADD COLUMN tokenId TEXT DEFAULT ''"; + +class SelectionConfigDataBase { +public: + static std::shared_ptr GetInstance(); + int64_t Insert(const OHOS::NativeRdb::ValuesBucket &insertValues); + int32_t Update(int32_t &changedRows, const OHOS::NativeRdb::ValuesBucket &values, + const OHOS::NativeRdb::RdbPredicates &predicates); + int32_t Update(int32_t &changedRows, const OHOS::NativeRdb::ValuesBucket &values, const std::string &whereClause, + const std::vector &whereArgs); + int32_t Delete(const OHOS::NativeRdb::RdbPredicates &rdbPredicates); + int32_t Delete(int32_t &changedRows, const std::string &whereClause, const std::vector &whereArgs); + int32_t ExecuteSql(const std::string &sql, + const std::vector &bindArgs = std::vector()); + std::shared_ptr QuerySql( + const std::string &sql, const std::vector &selectionArgs); + std::shared_ptr Query( + const OHOS::NativeRdb::AbsRdbPredicates &predicates, const std::vector &columns); + int32_t BeginTransaction(); + int32_t Commit(); + int32_t RollBack(); + +private: + SelectionConfigDataBase(const std::shared_ptr& store); + DISALLOW_COPY_AND_MOVE(SelectionConfigDataBase); + + static std::shared_ptr CreateStore(); + + static std::shared_ptr instance_; + std::shared_ptr store_; +}; + +class SelectionConfigDataBaseCallBack : public OHOS::NativeRdb::RdbOpenCallback { +public: + int32_t OnCreate(OHOS::NativeRdb::RdbStore &rdbStore) override; + int32_t OnUpgrade(OHOS::NativeRdb::RdbStore &rdbStore, int32_t oldVersion, int32_t newVersion) override; + int32_t OnDowngrade(OHOS::NativeRdb::RdbStore &rdbStore, int32_t currentVersion, int32_t targetVersion) override; +}; + +} // namespace SelectionFwk +} // namespace OHOS + +#endif // SELECTION_CONFIG_DATABASE_H diff --git a/service/include/selection_input_monitor.h b/service/include/selection_input_monitor.h new file mode 100644 index 0000000000000000000000000000000000000000..1bc5be75501b43f47a30d20dc2373d465a791ed0 --- /dev/null +++ b/service/include/selection_input_monitor.h @@ -0,0 +1,125 @@ +/* + * 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 SELECTION_INPUT_MONITOR_H +#define SELECTION_INPUT_MONITOR_H + +#include +#include +#include +#include "selection_interface.h" +#include "pasteboard_disposable_observer.h" +namespace OHOS::SelectionFwk { +using namespace MMI; +using namespace OHOS::MiscServices; + +constexpr const uint32_t DOUBLE_CLICK_TIME = 500; +constexpr const uint32_t MAX_PASTERBOARD_TEXT_LENGTH = 2000; +constexpr const uint32_t BYTES_PER_CHINESE_CHAR = 3; // In UTF-8, common Chinese characters occupy 3 bytes. + +enum class SelectInputState: uint32_t { + SELECT_INPUT_INITIAL = 0, + SELECT_INPUT_WORD_BEGIN = 1, + SELECT_INPUT_WAIT_LEFT_MOVE = 2, + SELECT_INPUT_LEFT_MOVE = 3, + SELECT_INPUT_WAIT_DOUBLE_CLICK = 4, + SELECT_INPUT_WAIT_TRIPLE_CLICK = 5, + SELECT_INPUT_DOUBLE_CLICKED = 6, + SELECT_INPUT_TRIPLE_CLICKED = 7, +}; + +enum class SelectInputSubState: uint32_t { + SUB_INITIAL = 0, + SUB_WAIT_POINTER_ACTION_BUTTON_DOWN = 1, + SUB_WAIT_POINTER_ACTION_BUTTON_UP = 2, + SUB_WAIT_KEY_CTRL_DOWN = 3, + SUB_WAIT_KEY_CTRL_UP = 4, +}; + +class BaseSelectionInputMonitor : public IInputEventConsumer { +public: + BaseSelectionInputMonitor() { + } + + virtual void OnInputEvent(std::shared_ptr keyEvent) const; + virtual void OnInputEvent(std::shared_ptr pointerEvent) const; + virtual void OnInputEvent(std::shared_ptr axisEvent) const; + + void ResetFinishedState() const; + bool IsTextSelected() const; + const SelectionInfo& GetSelectionInfo() const; + +public: + static bool ctrlSelectFlag; + +private: + void InputInitialProcess(std::shared_ptr pointerEvent) const; + void InputWordBeginProcess(std::shared_ptr pointerEvent) const; + void InputWordWaitLeftMoveProcess(std::shared_ptr pointerEvent) const; + void InputWordWaitDoubleClickProcess(std::shared_ptr pointerEvent) const; + void InputWordJudgeTripleClickProcess(std::shared_ptr pointerEvent) const; + void InputWordWaitTripleClickProcess(std::shared_ptr pointerEvent) const; + void FinishedWordSelection() const; + void ResetProcess(std::shared_ptr pointerEvent) const; + void ResetState() const; + void JudgeTripleClick() const; + void SaveSelectionStartInfo(std::shared_ptr pointerEvent) const; + void SaveSelectionEndInfo(std::shared_ptr pointerEvent) const; + void SaveSelectionType() const; + bool IsSelectionDone() const; + +private: + mutable SelectInputState curSelectState = SelectInputState::SELECT_INPUT_INITIAL; + mutable SelectInputSubState subSelectState = SelectInputSubState::SUB_INITIAL; + mutable int64_t lastClickTime = 0; + mutable bool isTextSelected_ = false; + mutable SelectionInfo selectionInfo_; +}; + +class SelectionPasteboardDisposableObserver : public PasteboardDisposableObserver { +public: + SelectionPasteboardDisposableObserver(const std::shared_ptr &baseInputMonitor) + : baseInputMonitor_(baseInputMonitor) { + } + virtual ~SelectionPasteboardDisposableObserver() = default; + + void OnTextReceived(const std::string &text, int32_t errCode) override; + +private: + std::shared_ptr baseInputMonitor_; +}; + +class SelectionInputMonitor : public IInputEventConsumer { +public: + SelectionInputMonitor(); + + virtual void OnInputEvent(std::shared_ptr keyEvent) const; + virtual void OnInputEvent(std::shared_ptr pointerEvent) const; + virtual void OnInputEvent(std::shared_ptr axisEvent) const; + +private: + void FinishedWordSelection() const; + void InjectCtrlC() const; + void HandleWindowFocused(std::shared_ptr pointerEvent) const; + bool IsAppInBlacklist(const std::string& bundleName) const; + +private: + std::shared_ptr baseInputMonitor_; + mutable sptr pasteboardObserver_; + std::vector appBlacklist_; +}; +} + +#endif // SELECTION_INPUT_MONITOR_H \ No newline at end of file diff --git a/service/include/selection_service.h b/service/include/selection_service.h new file mode 100644 index 0000000000000000000000000000000000000000..70feea520bcd8a1ce2c8e5469000223f3a431abc --- /dev/null +++ b/service/include/selection_service.h @@ -0,0 +1,96 @@ +/* + * 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 SELECTION_SERVICE_H +#define SELECTION_SERVICE_H + +#include +#include + +#include "ability_connect_callback_stub.h" +#include "callback_object.h" +#include "focus_change_info.h" +#include "iselection_listener.h" +#include "selection_service_stub.h" +#include "refbase.h" +#include "system_ability.h" +#include + +namespace OHOS::SelectionFwk { +using namespace MMI; + +constexpr const char *SYS_SELECTION_SWITCH = "sys.selection.switch"; +constexpr const char *SYS_SELECTION_TRIGGER = "sys.selection.trigger"; +constexpr const char *SYS_SELECTION_APP = "sys.selection.app"; +constexpr const char *DEFAULT_SWITCH = "on"; +constexpr const char *DEFAULT_TRIGGER = "ctrl"; +constexpr const char *DEFAULT_SELECTION_APP = "com.hm.youdao/ExtensionAbility"; + +class SelectionExtensionAbilityConnection : public OHOS::AAFwk::AbilityConnectionStub { +public: + SelectionExtensionAbilityConnection() = default; + ~SelectionExtensionAbilityConnection() = default; + void OnAbilityConnectDone( + const OHOS::AppExecFwk::ElementName &element, const sptr &remoteObject, int resultCode) override; + void OnAbilityDisconnectDone(const OHOS::AppExecFwk::ElementName &element, int resultCode) override; +}; + +class SelectionService : public SystemAbility, public SelectionServiceStub { + DECLARE_SYSTEM_ABILITY(SelectionService); + +public: + static sptr GetInstance(); + SelectionService(); + SelectionService(int32_t saId, bool runOnCreate); + ~SelectionService(); + + ErrCode RegisterListener(const sptr& listener) override; + ErrCode UnregisterListener(const sptr& listener) override; + ErrCode IsCurrentSelectionApp(int pid, bool &resultValue) override; + int32_t Dump(int32_t fd, const std::vector &args) override; + int32_t ConnectNewExtAbility(const std::string& bundleName, const std::string& abilityName); + void DisconnectCurrentExtAbility(); + sptr GetListener(); + void PersistSelectionConfig(); + +protected: + void OnStart() override; + void OnStop() override; + void HandleKeyEvent(int32_t keyCode); + void HandlePointEvent(int32_t type); + +private: + void InputMonitorInit(); + void InputMonitorCancel(); + void WatchParams(); + void InitFocusChangedMonitor(); + void CancelFocusChangedMonitor(); + void HandleFocusChanged(const sptr &focusChangeInfo, bool isFocused); + void SynchronizeSelectionConfig(); + int GetUserId(); + int LoadAccountLocalId(); + bool IsUserLoggedIn(); + + int32_t inputMonitorId_ {-1}; + mutable std::mutex mutex_; + static sptr listener_; + sptr connectInner_ {nullptr}; + std::mutex connectInnerMutex_; + std::atomic pid_ = -1; + std::atomic userId_ = -1; +}; +} + +#endif // SELECTION_SERVICE_H \ No newline at end of file diff --git a/service/include/sys_selection_config_repository.h b/service/include/sys_selection_config_repository.h new file mode 100644 index 0000000000000000000000000000000000000000..ba8217c4a22d626f8818804fb9c221b9f6a9c182 --- /dev/null +++ b/service/include/sys_selection_config_repository.h @@ -0,0 +1,44 @@ +/* + * 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 SYS_SELECTION_CONFIG_REPOSITORY_H +#define SYS_SELECTION_CONFIG_REPOSITORY_H +#include +#include +#include "selection_config.h" + +namespace OHOS { +namespace SelectionFwk { +class SysSelectionConfigRepository { +public: + static std::shared_ptr GetInstance(); + int SetSysParameters(const SelectionConfig &config); + SelectionConfig GetSysParameters(); + void DisableSAService(); + +private: + void SetEnabled(bool enabled); + void SetTriggered(bool isTriggered); + void SetUid(int uid); + void SetApplicationInfo(const std::string &applicationInfo); + int GetEnable(); + int GetTriggered(); + int GetUid(); + std::string GetApplicationInfo(); + static std::shared_ptr instance_; +}; +} // namespace SelectionFwk +} // namespace OHOS +#endif // SYS_SELECTION_CONFIG_REPOSITORY_H \ No newline at end of file diff --git a/service/libselection_service.map b/service/libselection_service.map new file mode 100644 index 0000000000000000000000000000000000000000..31f971927ee5bb7ae01ff0410e25d005d7dd82f1 --- /dev/null +++ b/service/libselection_service.map @@ -0,0 +1,10 @@ +{ + global: + extern "C++" { + OHOS::SelectionFwk::SelectionService::RegisterListener*; + OHOS::SelectionFwk::SelectionService::UnregisterListener*; + OHOS::SelectionFwk::SelectionService::IsCurrentSelectionApp*; + }; + local: + *; +}; \ No newline at end of file diff --git a/service/src/db_selection_config_repository.cpp b/service/src/db_selection_config_repository.cpp new file mode 100644 index 0000000000000000000000000000000000000000..671bf4f1560038b30fa2a0026c9895af8d45da53 --- /dev/null +++ b/service/src/db_selection_config_repository.cpp @@ -0,0 +1,229 @@ +/* + * 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 "db_selection_config_repository.h" + +#include + +#include "selection_errors.h" +#include "selection_config_database.h" +#include "selection_log.h" + +using namespace OHOS::NativeRdb; + +namespace OHOS { +namespace SelectionFwk { +std::shared_ptr DbSelectionConfigRepository::instance_ = nullptr; + +DbSelectionConfigRepository::DbSelectionConfigRepository() +{ + selectionDatabase_ = SelectionConfigDataBase::GetInstance(); +} + +std::shared_ptr DbSelectionConfigRepository::GetInstance() +{ + static std::mutex instanceMutex; + std::lock_guard guard(instanceMutex); + if (instance_ == nullptr) { + SELECTION_HILOGI("reset to new DbSelectionConfigRepository instance"); + instance_.reset(new DbSelectionConfigRepository()); + } + return instance_; +} + +int DbSelectionConfigRepository::Save(int uid, const SelectionConfig &info) +{ + if (selectionDatabase_ == nullptr) { + SELECTION_HILOGE("selectionDatabase_ is null"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + std::lock_guard guard(databaseMutex_); + ValuesBucket values; + values.Clear(); + values.PutInt("uid", uid); + values.PutInt("enable", info.GetEnable()); + values.PutInt("trigger", info.GetTriggered()); + values.PutString("bundleName", info.GetApplicationInfo()); + int ret = selectionDatabase_->BeginTransaction(); + if (ret < SELECTION_CONFIG_OK) { + SELECTION_HILOGE("BeginTransaction error: %{public}d", ret); + return ret; + } + + int changedRows = 0; + RdbPredicates predicates(SELECTION_CONFIG_TABLE_NAME); + predicates.EqualTo("uid", std::to_string(uid)); + + ret = selectionDatabase_->Update(changedRows, values, predicates); + if (ret != SELECTION_CONFIG_OK) { + SELECTION_HILOGE("Update error: %{public}d", ret); + return ret; + } + + if (changedRows == 0) { + ret = selectionDatabase_->Insert(values); + if (ret < SELECTION_CONFIG_OK) { + SELECTION_HILOGE("Insert error: %{public}d", ret); + (void)selectionDatabase_->RollBack(); + return ret; + } + } + ret = selectionDatabase_->Commit(); + if (ret < SELECTION_CONFIG_OK) { + SELECTION_HILOGE("Commit error: %{public}d", ret); + (void)selectionDatabase_->RollBack(); + return ret; + } + SELECTION_HILOGI("add success: enable=%{public}d trigger=%{public}d applicationInfo=%{public}s", + info.GetEnable(), info.GetTriggered(), info.GetApplicationInfo().c_str()); + return ret; +} + +std::optional DbSelectionConfigRepository::GetOneByUserId(int uid) +{ + SelectionConfig info; + std::lock_guard guard(databaseMutex_); + std::vector columns; + RdbPredicates rdbPredicates(SELECTION_CONFIG_TABLE_NAME); + rdbPredicates.EqualTo("uid", std::to_string(uid)); + if (GetConfigFromDatabase(rdbPredicates, columns, info) != SELECTION_CONFIG_OK) { + return std::nullopt; + } + SELECTION_HILOGI("enable=%{public}d trigger=%{public}d applicationInfo=%{public}s", + info.GetEnable(), info.GetTriggered(), info.GetApplicationInfo().c_str()); + return info; +} + +int DbSelectionConfigRepository::GetConfigFromDatabase(const RdbPredicates &rdbPredicates, + const std::vector &columns, SelectionConfig &info) +{ + if (selectionDatabase_ == nullptr) { + SELECTION_HILOGE("rightDatabase_ is null"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int ret = selectionDatabase_->BeginTransaction(); + if (ret < SELECTION_CONFIG_OK) { + SELECTION_HILOGE("BeginTransaction error: %{public}d", ret); + return ret; + } + auto resultSet = selectionDatabase_->Query(rdbPredicates, columns); + if (resultSet == nullptr) { + SELECTION_HILOGE("Query error"); + (void)selectionDatabase_->RollBack(); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + ret = selectionDatabase_->Commit(); + if (ret < SELECTION_CONFIG_OK) { + SELECTION_HILOGE("Commit error: %{public}d", ret); + (void)selectionDatabase_->RollBack(); + return ret; + } + int32_t rowCount = 0; + resultSet->GetRowCount(rowCount); + if (rowCount == 0) { + SELECTION_HILOGI("Can not found uid in selection_config table"); + return SELECTION_CONFIG_NOT_FOUND; + } + return ProcessQueryResult(resultSet, info); +} + +int DbSelectionConfigRepository::ProcessQueryResult(const std::shared_ptr &resultSet, + SelectionConfig &info) +{ + struct SelectionConfigTableInfo table; + int ret = RetrieveResultSetMetadata(resultSet, table); + if (ret < SELECTION_CONFIG_OK) { + SELECTION_HILOGE("GetResultSetTableInfo failed"); + return ret; + } + + bool endFlag = false; + int enable = 0; + std::string applicationInfo = ""; + int trigger = 0; + int uid = -1; + + for (int32_t i = 0; i < table.rowCount && !endFlag; i++, resultSet->IsEnded(endFlag)) { + if (resultSet->GoToRow(i) != E_OK) { + SELECTION_HILOGE("GoToRow %{public}d", i); + return -1; + } + if (resultSet->GetInt(table.enableIndex, enable) == E_OK && + resultSet->GetString(table.applicationInfoIndex, applicationInfo) == E_OK && + resultSet->GetInt(table.triggerIndex, trigger) == E_OK && + resultSet->GetInt(table.uidIndex, uid) == E_OK) { + info.SetEnabled(enable == 1? true : false); + info.SetTriggered(trigger == 1? true: false); + info.SetApplicationInfo(applicationInfo); + info.SetUid(uid); + } + SELECTION_HILOGI("enable=%{public}d trigger=%{public}d applicationInfo=%{public}s", + enable, trigger, applicationInfo.c_str()); + } + + int position = 0; + resultSet->GetRowIndex(position); + resultSet->IsEnded(endFlag); + SELECTION_HILOGI("row=%{public}d col=%{public}d pos=%{public}d end=%{public}s", + table.rowCount, table.columnCount, position, (endFlag ? "yes" : "no")); + return 0; +} + +int DbSelectionConfigRepository::RetrieveResultSetMetadata( + const std::shared_ptr &resultSet, struct SelectionConfigTableInfo &table) +{ + if (resultSet == nullptr) { + SELECTION_HILOGE("resultSet is null"); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + int32_t rowCount = 0; + int32_t columnCount = 0; + std::vector columnNames; + if (resultSet->GetRowCount(rowCount) != E_OK || resultSet->GetColumnCount(columnCount) != E_OK || + resultSet->GetAllColumnNames(columnNames) != E_OK) { + SELECTION_HILOGE("get table info failed"); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + int32_t columnNamesCount = static_cast(columnNames.size()); + for (int32_t i = 0; i < columnNamesCount; i++) { + std::string &columnName = columnNames.at(i); + if (columnName == "id") { + table.primaryKeyIndex = i; + } + if (columnName == "uid") { + table.uidIndex = i; + } + if (columnName == "enable") { + table.enableIndex = i; + } + if (columnName == "bundleName") { + table.applicationInfoIndex = i; + } + if (columnName == "trigger") { + table.triggerIndex = i; + } + if (columnName == "shortcutKeys") { + table.shortcutKeysIndex = i; + } + } + table.rowCount = rowCount; + table.columnCount = columnCount; + SELECTION_HILOGI("info[%{public}d/%{public}d]: %{public}d/%{public}d/%{public}d/%{public}d/%{public}d/%{public}d", + rowCount, columnCount, table.primaryKeyIndex, table.uidIndex, table.enableIndex, table.applicationInfoIndex, + table.triggerIndex, table.shortcutKeysIndex); + return SELECTION_CONFIG_OK; +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/service/src/selection_app_validator.cpp b/service/src/selection_app_validator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..79613856b4e8f6d1fea433b6534904253aa2fb5b --- /dev/null +++ b/service/src/selection_app_validator.cpp @@ -0,0 +1,79 @@ +/* + * 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 "selection_app_validator.h" + +#include +#include "accesstoken_kit.h" +#include "parameter.h" +#include "param_wrapper.h" +#include "selection_log.h" + +namespace OHOS::SelectionFwk { + +static const char* SELECTION_APPLICATION = "sys.selection.app"; + +SelectionAppValidator& SelectionAppValidator::GetInstance() +{ + static SelectionAppValidator instance; + return instance; +} + +bool SelectionAppValidator::Validate() const +{ + auto currentBundleName = GetCurrentBundleName(); + if (!currentBundleName.has_value()) { + SELECTION_HILOGE("Failed to GetCurrentBundleName"); + return false; + } + auto bundleNameFromSys = GetBundleNameFromSys(); + if (!bundleNameFromSys.has_value()) { + SELECTION_HILOGE("Failed to GetBundleNameFromSys"); + return false; + } + SELECTION_HILOGI("Validate bundleName: [%{public}s] with bundleName: [%{public}s] from sys", + currentBundleName.value().c_str(), bundleNameFromSys.value().c_str()); + return currentBundleName.value() == bundleNameFromSys.value(); +} + +std::optional SelectionAppValidator::GetCurrentBundleName() const +{ + auto callingTokenId = IPCSkeleton::GetCallingTokenID(); + OHOS::Security::AccessToken::HapTokenInfo hapTokenInfoRes; + int32_t ret = OHOS::Security::AccessToken::AccessTokenKit::GetHapTokenInfo(callingTokenId, hapTokenInfoRes); + if (ret != 0) { + SELECTION_HILOGE("Failed to GetHapTokenInfo, ret = %{public}d.", ret); + return std::nullopt; + } + return hapTokenInfoRes.bundleName; +} + +std::optional SelectionAppValidator::GetBundleNameFromSys() const +{ + std::string selectionApp; + if (OHOS::system::GetStringParameter(SELECTION_APPLICATION, selectionApp) != 0) { + SELECTION_HILOGE("GetStringParameter failed for SELECTION_APPLICATION"); + return std::nullopt; + } + + const auto pos = selectionApp.find('/'); + if (pos == std::string::npos) { + SELECTION_HILOGE("%{public}s: [%{public}s] is invalid", SELECTION_APPLICATION, selectionApp.c_str()); + return std::nullopt; + } + return selectionApp.substr(0, pos); +} + +} // namespace OHOS::SelectionFwk \ No newline at end of file diff --git a/service/src/selection_common.cpp b/service/src/selection_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a21e1c63e5577a25abd8023130063f81486c3e00 --- /dev/null +++ b/service/src/selection_common.cpp @@ -0,0 +1,36 @@ +/* + * 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 + +bool isNumber(const std::string& str) { + if (str.empty()) { + return false; + } + + size_t i = 0; + if (str[i] == '+' || str[i] == '-') { + i++; + } + + if (i != 0 && str.length() == 1) { + return false; + } + + if (std::all_of(str.begin() + i, str.end(), [](unsigned char c) { return std::isdigit(c); })) { + return true; + } + + return false; +} \ No newline at end of file diff --git a/service/src/selection_config.cpp b/service/src/selection_config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d206d54fabf9ad5e3ca799eeef503f3717e0f9aa --- /dev/null +++ b/service/src/selection_config.cpp @@ -0,0 +1,124 @@ +/* + * 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 "selection_config.h" + +#include +#include + +namespace OHOS { +namespace SelectionFwk { +bool SelectionConfig::GetEnable() const +{ + return isEnabled_; +} + +bool SelectionConfig::GetTriggered() const +{ + return isTriggered_; +} + +int SelectionConfig::GetUid() const +{ + return uid_; +} + +std::string SelectionConfig::GetApplicationInfo() const +{ + return applicationInfo_; +} + +void SelectionConfig::SetEnabled(bool enabled) +{ + isEnabled_ = enabled; +} + +void SelectionConfig::SetTriggered(bool isTriggered) +{ + isTriggered_ = isTriggered; +} + +void SelectionConfig::SetUid(int uid) +{ + uid_ = uid; +} + +void SelectionConfig::SetApplicationInfo(const std::string &applicationInfo) +{ + applicationInfo_ = applicationInfo; +} + +std::string SelectionConfig::ToString() const { + std::ostringstream oss; + oss << "enable: " << isEnabled_ << ", trigger: " + << isTriggered_ << ", applicationInfo: " << applicationInfo_; + return oss.str(); +} + +MemSelectionConfig& MemSelectionConfig::GetInstance() +{ + static MemSelectionConfig instance; + return instance; +} + +void MemSelectionConfig::SetSelectionConfig(const SelectionConfig &config) +{ + std::lock_guard lock(mutex_); + delegate_ = config; +} + +SelectionConfig& MemSelectionConfig::GetSelectionConfig() +{ + std::lock_guard lock(mutex_); + return delegate_; +} + +bool MemSelectionConfig::GetEnable() const +{ + std::lock_guard lock(mutex_); + return delegate_.GetEnable(); +} + +bool MemSelectionConfig::GetTriggered() const +{ + std::lock_guard lock(mutex_); + return delegate_.GetTriggered(); +} + +std::string MemSelectionConfig::GetApplicationInfo() const +{ + std::lock_guard lock(mutex_); + return delegate_.GetApplicationInfo(); +} + +void MemSelectionConfig::SetEnabled(bool enabled) +{ + std::lock_guard lock(mutex_); + delegate_.SetEnabled(enabled); +} + +void MemSelectionConfig::SetTriggered(bool isTriggered) +{ + std::lock_guard lock(mutex_); + delegate_.SetTriggered(isTriggered); +} + +void MemSelectionConfig::SetApplicationInfo(const std::string &applicationInfo) +{ + std::lock_guard lock(mutex_); + delegate_.SetApplicationInfo(applicationInfo); +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/service/src/selection_config_comparator.cpp b/service/src/selection_config_comparator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c258479af10daf6c0df523b9ec6b516283044574 --- /dev/null +++ b/service/src/selection_config_comparator.cpp @@ -0,0 +1,85 @@ +/* + * 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 +#include "selection_config_comparator.h" +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { + +std::string ComparisionResult::ToString() const { + std::ostringstream oss; + oss << "ComparisionResult: shouldCreate: " << shouldCreate + << " shouldStop: " << shouldStop + << " direction: " << static_cast(direction) + << " selectionConfig: [ " << selectionConfig.ToString() << "]"; + return oss.str(); +} + +ComparisionResult SelectionConfigComparator::Compare(int uid, const SelectionConfig &sysSelectionConfig, + std::optional &dbSelectionConfig) +{ + if (dbSelectionConfig.has_value()) { + SELECTION_HILOGI("dbSelectionConfig has value"); + } else { + SELECTION_HILOGI("dbSelectionConfig is nullopt!"); + } + auto result = DoCompare(uid, sysSelectionConfig, dbSelectionConfig); + SELECTION_HILOGI("result: %{public}s", result.ToString().c_str()); + return result; +} + +ComparisionResult SelectionConfigComparator::DoCompare(int uid, const SelectionConfig &sysSelectionConfig, + std::optional &dbSelectionConfig) +{ + ComparisionResult result; + if (!dbSelectionConfig.has_value()) { + result.shouldCreate = true; + result.selectionConfig = sysSelectionConfig; + result.selectionConfig.SetUid(uid); + return result; + } + + if (dbSelectionConfig.value().GetEnable()) { + if (sysSelectionConfig.GetUid() != uid) { + result.direction = FromDbToSys; + result.selectionConfig = dbSelectionConfig.value(); + return result; + } + result.direction = FromSysToDb; + result.selectionConfig = sysSelectionConfig; + if (!sysSelectionConfig.GetEnable()) { + result.shouldStop = true; + } + return result; + } + + if (sysSelectionConfig.GetUid() != uid) { + result.selectionConfig = dbSelectionConfig.value(); + result.shouldStop = true; + return result; + } + + result.direction = FromSysToDb; + result.selectionConfig = sysSelectionConfig; + if (!sysSelectionConfig.GetEnable()) { + result.shouldStop = true; + } + + return result; +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/service/src/selection_config_database.cpp b/service/src/selection_config_database.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1ca6b88db57fd6448d6a4547e9a98ccf3a43345 --- /dev/null +++ b/service/src/selection_config_database.cpp @@ -0,0 +1,255 @@ +/* + * 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 "selection_config_database.h" + +#include "selection_errors.h" +#include "selection_log.h" + +namespace OHOS { +namespace SelectionFwk { + +std::shared_ptr SelectionConfigDataBase::instance_ = nullptr; + +SelectionConfigDataBase::SelectionConfigDataBase(const std::shared_ptr& store) + : store_(store) +{ +} + +std::shared_ptr SelectionConfigDataBase::GetInstance() +{ + static std::mutex instanceMutex; + std::lock_guard guard(instanceMutex); + if (instance_ == nullptr) { + SELECTION_HILOGI("reset to new SelectionConfigDataBase instance"); + auto store = SelectionConfigDataBase::CreateStore(); + if (store != nullptr) { + instance_.reset(new SelectionConfigDataBase(store)); + } + return instance_; + } + return instance_; +} + +int32_t SelectionConfigDataBase::BeginTransaction() +{ + if (store_ == nullptr) { + SELECTION_HILOGE("BeginTransaction store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int32_t ret = store_->BeginTransaction(); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("BeginTransaction fail :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +int32_t SelectionConfigDataBase::Commit() +{ + if (store_ == nullptr) { + SELECTION_HILOGE("Commit store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int32_t ret = store_->Commit(); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("Commit fail :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +int32_t SelectionConfigDataBase::RollBack() +{ + if (store_ == nullptr) { + SELECTION_HILOGE("RollBack store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int32_t ret = store_->RollBack(); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("RollBack fail :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +int64_t SelectionConfigDataBase::Insert(const OHOS::NativeRdb::ValuesBucket &insertValues) +{ + if (store_ == nullptr) { + SELECTION_HILOGE("Insert store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int64_t outRowId = 0; + int32_t ret = store_->Insert(outRowId, SELECTION_CONFIG_TABLE_NAME, insertValues); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("Insert ret :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + SELECTION_HILOGI("Insert id=%{public}" PRIu64 "", outRowId); + return outRowId; +} + +int32_t SelectionConfigDataBase::Update( + int32_t &changedRows, const OHOS::NativeRdb::ValuesBucket &values, const OHOS::NativeRdb::RdbPredicates &predicates) +{ + if (store_ == nullptr) { + SELECTION_HILOGE("Update(RdbPredicates) store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int32_t ret = store_->Update(changedRows, values, predicates); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("Update(RdbPredicates) ret :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +int32_t SelectionConfigDataBase::Update(int32_t &changedRows, const OHOS::NativeRdb::ValuesBucket &values, + const std::string &whereClause, const std::vector &whereArgs) +{ + if (store_ == nullptr) { + SELECTION_HILOGE("Update(whereClause) store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int32_t ret = store_->Update(changedRows, SELECTION_CONFIG_TABLE_NAME, values, whereClause, whereArgs); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("Update(whereClause) ret :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +int32_t SelectionConfigDataBase::Delete(const OHOS::NativeRdb::RdbPredicates &predicates) +{ + if (store_ == nullptr) { + SELECTION_HILOGE("Delete(RdbPredicates) store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int32_t deleteRow = 0; + int32_t ret = store_->Delete(deleteRow, predicates); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("Delete(RdbPredicates) ret :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +int32_t SelectionConfigDataBase::Delete( + int32_t &changedRows, const std::string &whereClause, const std::vector &whereArgs) +{ + if (store_ == nullptr) { + SELECTION_HILOGE("Delete store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int32_t ret = store_->Delete(changedRows, SELECTION_CONFIG_TABLE_NAME, whereClause, whereArgs); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("Delete(whereClause) ret :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +int32_t SelectionConfigDataBase::ExecuteSql(const std::string &sql, + const std::vector &bindArgs) +{ + if (store_ == nullptr) { + SELECTION_HILOGE("ExecuteSql store_ is nullptr"); + return SELECTION_CONFIG_RDB_NO_INIT; + } + int32_t ret = store_->ExecuteSql(sql, bindArgs); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("ExecuteSql ret :%{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +std::shared_ptr SelectionConfigDataBase::QuerySql( + const std::string &sql, const std::vector &selectionArgs) +{ + if (store_ == nullptr) { + SELECTION_HILOGE("QuerySql(sql) store_ is nullptr"); + return nullptr; + } + return store_->QuerySql(sql); +} + +std::shared_ptr SelectionConfigDataBase::Query( + const OHOS::NativeRdb::AbsRdbPredicates &predicates, const std::vector &columns) +{ + if (store_ == nullptr) { + SELECTION_HILOGE("Query(AbsRdbPredicates) store_ is nullptr"); + return nullptr; + } + return store_->Query(predicates, columns); +} + +std::shared_ptr SelectionConfigDataBase::CreateStore() { + std::string selectionDatabaseName = SELECTION_CONFIG_DB_PATH + SELECTION_CONFIG_DB_NAME; + int32_t errCode = OHOS::NativeRdb::E_OK; + OHOS::NativeRdb::RdbStoreConfig config(selectionDatabaseName); + config.SetSecurityLevel(NativeRdb::SecurityLevel::S1); + SelectionConfigDataBaseCallBack sqliteOpenHelperCallback; + auto store = OHOS::NativeRdb::RdbHelper::GetRdbStore(config, DATABASE_OPEN_VERSION, + sqliteOpenHelperCallback, errCode); + if (errCode != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("GetRdbStore errCode :%{public}d", errCode); + } else { + SELECTION_HILOGI("GetRdbStore success :%{public}d", errCode); + } + return store; +} + +int32_t SelectionConfigDataBaseCallBack::OnCreate(OHOS::NativeRdb::RdbStore &store) +{ + std::string sql = CREATE_SELECTION_CONFIG_TABLE; + int32_t ret = store.ExecuteSql(sql); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("OnCreate failed: %{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + SELECTION_HILOGI("DB OnCreate Done: %{public}d", ret); + return SELECTION_CONFIG_OK; +} + +int32_t SelectionConfigDataBaseCallBack::OnUpgrade(OHOS::NativeRdb::RdbStore &store, int32_t oldVersion, + int32_t newVersion) +{ + SELECTION_HILOGI("DB OnUpgrade Enter %{public}d => %{public}d", oldVersion, newVersion); + if (oldVersion >= newVersion) { + return SELECTION_CONFIG_OK; + } + + std::string sql = SQL_ADD_TOKEN_ID; + int32_t ret = store.ExecuteSql(sql); + if (ret != OHOS::NativeRdb::E_OK) { + SELECTION_HILOGE("DB OnUpgrade failed: %{public}d", ret); + return SELECTION_CONFIG_RDB_EXECUTE_FAILTURE; + } + return SELECTION_CONFIG_OK; +} + +int32_t SelectionConfigDataBaseCallBack::OnDowngrade(OHOS::NativeRdb::RdbStore &store, int32_t oldVersion, + int32_t newVersion) +{ + SELECTION_HILOGI("DB OnDowngrade Enter"); + (void)store; + (void)oldVersion; + (void)newVersion; + return SELECTION_CONFIG_OK; +} + +} // namespace SelectionFwk +} // namespace OHOS diff --git a/service/src/selection_input_monitor.cpp b/service/src/selection_input_monitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e671c826db2faddba2683fe8aa171e681607c8c --- /dev/null +++ b/service/src/selection_input_monitor.cpp @@ -0,0 +1,582 @@ +/* + * 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 "selection_service.h" + +#include "selection_log.h" +#include +#include "common_event_manager.h" +#include "pasteboard_client.h" +#include "selection_config.h" +#include "selection_input_monitor.h" +#include "window_manager.h" + +using namespace OHOS; +using namespace OHOS::SelectionFwk; +using namespace OHOS::AppExecFwk; +using namespace OHOS::MMI; +using namespace OHOS::EventFwk; + +bool BaseSelectionInputMonitor::ctrlSelectFlag = false; +std::atomic selSeqId = 0; + +static int64_t GetCurrentTimeMillis() { + auto now = std::chrono::system_clock::now(); + auto duration = now.time_since_epoch(); + return std::chrono::duration_cast(duration).count(); +} + +uint32_t GenerateSequenceId() +{ + return selSeqId.fetch_add(1, std::memory_order_seq_cst); +} + +bool BaseSelectionInputMonitor::IsTextSelected() const { + return isTextSelected_; +} + +const SelectionInfo& BaseSelectionInputMonitor::GetSelectionInfo() const { + return selectionInfo_; +} + +void BaseSelectionInputMonitor::OnInputEvent(std::shared_ptr keyEvent) const +{ + if (!ctrlSelectFlag) { + return; + } + + if (subSelectState != SelectInputSubState::SUB_WAIT_KEY_CTRL_DOWN && + subSelectState != SelectInputSubState::SUB_WAIT_KEY_CTRL_UP) { + return; + } + + int32_t keyCode = keyEvent->GetKeyCode(); + int32_t action = keyEvent->GetKeyAction(); + if (keyCode != KeyEvent::KEYCODE_CTRL_LEFT && keyCode != KeyEvent::KEYCODE_CTRL_RIGHT) { + curSelectState = SelectInputState::SELECT_INPUT_INITIAL; + subSelectState = SelectInputSubState::SUB_INITIAL; + SELECTION_HILOGI("[SelectionService] Set curSelectState SELECT_INPUT_INITIAL."); + return; + } + SELECTION_HILOGI("[SelectionService] Processed ctrl key."); + if (subSelectState == SelectInputSubState::SUB_WAIT_KEY_CTRL_DOWN && action == KeyEvent::KEY_ACTION_DOWN) { + subSelectState = SelectInputSubState::SUB_WAIT_KEY_CTRL_UP; + return; + } + + if (subSelectState == SelectInputSubState::SUB_WAIT_KEY_CTRL_UP && action == KeyEvent::KEY_ACTION_UP) { + if (curSelectState == SelectInputState::SELECT_INPUT_WAIT_LEFT_MOVE) { + curSelectState = SelectInputState::SELECT_INPUT_LEFT_MOVE; + SELECTION_HILOGI("[SelectionService] Set curSelectState SELECT_INPUT_LEFT_MOVE."); + } else if (curSelectState == SelectInputState::SELECT_INPUT_WAIT_DOUBLE_CLICK) { + curSelectState = SelectInputState::SELECT_INPUT_DOUBLE_CLICKED; + SELECTION_HILOGI("[SelectionService] Set curSelectState SELECT_INPUT_DOUBLE_CLICKED."); + } else if (curSelectState == SelectInputState::SELECT_INPUT_WAIT_TRIPLE_CLICK) { + curSelectState = SelectInputState::SELECT_INPUT_TRIPLE_CLICKED; + SELECTION_HILOGI("[SelectionService] Set curSelectState SELECT_INPUT_DOUBLE_CLICKED."); + } + subSelectState = SelectInputSubState::SUB_INITIAL; + } else { + curSelectState = SelectInputState::SELECT_INPUT_INITIAL; + subSelectState = SelectInputSubState::SUB_INITIAL; + SELECTION_HILOGI("[SelectionService] Set curSelectState SELECT_INPUT_INITIAL."); + } + FinishedWordSelection(); + return; +} + +void BaseSelectionInputMonitor::OnInputEvent(std::shared_ptr pointerEvent) const +{ + int32_t buttonId = pointerEvent->GetButtonId(); + if (subSelectState == SelectInputSubState::SUB_WAIT_KEY_CTRL_DOWN || + subSelectState == SelectInputSubState::SUB_WAIT_KEY_CTRL_UP) { + int32_t action = pointerEvent->GetPointerAction(); + if (buttonId == PointerEvent::BUTTON_NONE && action == PointerEvent::POINTER_ACTION_MOVE) { + return; + } + } + if (buttonId != PointerEvent::MOUSE_BUTTON_LEFT) { + ResetState(); + } + SELECTION_HILOGD("[SelectionService] into PointerEvent, curSelectState = %{public}d.", curSelectState); + + switch (curSelectState) + { + case SelectInputState::SELECT_INPUT_INITIAL: + InputInitialProcess(pointerEvent); + break; + + case SelectInputState::SELECT_INPUT_WORD_BEGIN: + InputWordBeginProcess(pointerEvent); + break; + + case SelectInputState::SELECT_INPUT_WAIT_LEFT_MOVE: + InputWordWaitLeftMoveProcess(pointerEvent); + break; + + case SelectInputState::SELECT_INPUT_WAIT_DOUBLE_CLICK: + InputWordWaitDoubleClickProcess(pointerEvent); + break; + + case SelectInputState::SELECT_INPUT_DOUBLE_CLICKED: + InputWordJudgeTripleClickProcess(pointerEvent); + break; + + case SelectInputState::SELECT_INPUT_WAIT_TRIPLE_CLICK: + InputWordWaitTripleClickProcess(pointerEvent); + break; + + default: + break; + } + + FinishedWordSelection(); + return; +} + +void BaseSelectionInputMonitor::OnInputEvent(std::shared_ptr axisEvent) const +{ + SELECTION_HILOGI("[SelectionService] into axisEvent"); +}; + +void BaseSelectionInputMonitor::ResetProcess(std::shared_ptr pointerEvent) const +{ + curSelectState = SelectInputState::SELECT_INPUT_INITIAL; + subSelectState = SelectInputSubState::SUB_INITIAL; + OnInputEvent(pointerEvent); +} + +void BaseSelectionInputMonitor::SaveSelectionStartInfo(std::shared_ptr pointerEvent) const +{ + int32_t pointerId = pointerEvent->GetPointerId(); + PointerEvent::PointerItem pointerItem; + pointerEvent->GetPointerItem(pointerId, pointerItem); + selectionInfo_.startDisplayX = pointerItem.GetDisplayX(); + selectionInfo_.startDisplayY = pointerItem.GetDisplayY(); + selectionInfo_.endDisplayX = pointerItem.GetDisplayX(); + selectionInfo_.endDisplayY = pointerItem.GetDisplayY(); + selectionInfo_.startWindowX = pointerItem.GetWindowX(); + selectionInfo_.startWindowY = pointerItem.GetWindowY(); + selectionInfo_.endWindowX = pointerItem.GetWindowX(); + selectionInfo_.endWindowY = pointerItem.GetWindowY(); + selectionInfo_.displayId = pointerEvent->GetTargetDisplayId(); + selectionInfo_.windowId = pointerEvent->GetTargetWindowId(); + + OHOS::Rosen::WindowInfoOption windowInfoOption; + windowInfoOption.displayId = selectionInfo_.displayId; + windowInfoOption.windowId = selectionInfo_.windowId; + std::vector> infos; + Rosen::WMError ret = Rosen::WindowManager::GetInstance().ListWindowInfo(windowInfoOption, infos); + SELECTION_HILOGI("ListWindowInfo ret: %{public}d, infos size: %{public}zu", ret, infos.size()); + for (unsigned int i = 0; i < infos.size(); i++) { + auto info = infos[i]; + SELECTION_HILOGI("ListWindowInfo bundleName: %{public}s, windowtype:%{public}d, width:%{public}d, \ + height:%{public}d", info->windowMetaInfo.bundleName.c_str(), info->windowMetaInfo.windowType, + info->windowLayoutInfo.rect.width_, info->windowLayoutInfo.rect.height_); + } + if (ret == Rosen::WMError::WM_OK && infos.size() > 0) { + selectionInfo_.bundleName = infos[0]->windowMetaInfo.bundleName; + } +} + +void BaseSelectionInputMonitor::SaveSelectionEndInfo(std::shared_ptr pointerEvent) const +{ + if (curSelectState != SelectInputState::SELECT_INPUT_LEFT_MOVE && + curSelectState != SelectInputState::SELECT_INPUT_WAIT_LEFT_MOVE) { + return; + } + int32_t pointerId = pointerEvent->GetPointerId(); + PointerEvent::PointerItem pointerItem; + pointerEvent->GetPointerItem(pointerId, pointerItem); + selectionInfo_.endDisplayX = pointerItem.GetDisplayX(); + selectionInfo_.endDisplayY = pointerItem.GetDisplayY(); + selectionInfo_.endWindowX = pointerItem.GetWindowX(); + selectionInfo_.endWindowY = pointerItem.GetWindowY(); +} + +void BaseSelectionInputMonitor::SaveSelectionType() const +{ + switch (curSelectState) { + case SelectInputState::SELECT_INPUT_LEFT_MOVE: + selectionInfo_.selectionType = MOVE_SELECTION; + break; + + case SelectInputState::SELECT_INPUT_DOUBLE_CLICKED: + selectionInfo_.selectionType = DOUBLE_CLICKED_SELECTION; + break; + + case SelectInputState::SELECT_INPUT_TRIPLE_CLICKED: + selectionInfo_.selectionType = TRIPLE_CLICKED_SELECTION; + break; + + default: + break; + } +} + +bool BaseSelectionInputMonitor::IsSelectionDone() const { + if (curSelectState != SelectInputState::SELECT_INPUT_LEFT_MOVE && + curSelectState != SelectInputState::SELECT_INPUT_DOUBLE_CLICKED && + curSelectState != SelectInputState::SELECT_INPUT_TRIPLE_CLICKED) { + return false; + } + return true; +} + +void BaseSelectionInputMonitor::InputInitialProcess(std::shared_ptr pointerEvent) const +{ + int32_t action = pointerEvent->GetPointerAction(); + int32_t buttonId = pointerEvent->GetButtonId(); + if (action == PointerEvent::POINTER_ACTION_BUTTON_DOWN && buttonId == PointerEvent::MOUSE_BUTTON_LEFT) { + curSelectState = SelectInputState::SELECT_INPUT_WORD_BEGIN; + subSelectState = SelectInputSubState::SUB_INITIAL; + lastClickTime = GetCurrentTimeMillis(); + SaveSelectionStartInfo(pointerEvent); + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_WORD_BEGIN."); + } + return; +} + +void BaseSelectionInputMonitor::InputWordBeginProcess(std::shared_ptr pointerEvent) const +{ + int32_t action = pointerEvent->GetPointerAction(); + if (action == PointerEvent::POINTER_ACTION_MOVE) { + curSelectState = SelectInputState::SELECT_INPUT_WAIT_LEFT_MOVE; + subSelectState = SelectInputSubState::SUB_WAIT_POINTER_ACTION_BUTTON_UP; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_WAIT_LEFT_MOVE."); + } else if (action == PointerEvent::POINTER_ACTION_BUTTON_UP) { + curSelectState = SelectInputState::SELECT_INPUT_WAIT_DOUBLE_CLICK; + subSelectState = SelectInputSubState::SUB_WAIT_POINTER_ACTION_BUTTON_DOWN; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_WAIT_DOUBLE_CLICK."); + } + return; +} + +void BaseSelectionInputMonitor::InputWordWaitLeftMoveProcess(std::shared_ptr pointerEvent) const +{ + int32_t action = pointerEvent->GetPointerAction(); + if (action == PointerEvent::POINTER_ACTION_BUTTON_UP) { + if (ctrlSelectFlag) { + subSelectState = SelectInputSubState::SUB_WAIT_KEY_CTRL_DOWN; + SELECTION_HILOGI("set subSelectState to SUB_WAIT_KEY_CTRL_DOWN."); + } else { + curSelectState = SelectInputState::SELECT_INPUT_LEFT_MOVE; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_LEFT_MOVE."); + } + SaveSelectionEndInfo(pointerEvent); + } else if (action != PointerEvent::POINTER_ACTION_MOVE) { + SELECTION_HILOGI("Action reset. subSelectState is %{public}d, action is %{public}d.", subSelectState, action); + ResetProcess(pointerEvent); + } + return; +} + +void BaseSelectionInputMonitor::JudgeTripleClick() const +{ + auto curTime = GetCurrentTimeMillis(); + if (curTime - lastClickTime < DOUBLE_CLICK_TIME) { + curSelectState = SelectInputState::SELECT_INPUT_WAIT_TRIPLE_CLICK; + subSelectState = SelectInputSubState::SUB_WAIT_POINTER_ACTION_BUTTON_UP; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_WAIT_TRIPLE_CLICK."); + } else { + curSelectState = SelectInputState::SELECT_INPUT_WORD_BEGIN; + subSelectState = SelectInputSubState::SUB_INITIAL; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_WORD_BEGIN."); + } + lastClickTime = curTime; +} + +void BaseSelectionInputMonitor::InputWordWaitDoubleClickProcess(std::shared_ptr pointerEvent) const +{ + int32_t action = pointerEvent->GetPointerAction(); + if (subSelectState == SelectInputSubState::SUB_WAIT_POINTER_ACTION_BUTTON_DOWN && + action == PointerEvent::POINTER_ACTION_BUTTON_DOWN) { + auto curTime = GetCurrentTimeMillis(); + if (curTime - lastClickTime < DOUBLE_CLICK_TIME) { + subSelectState = SelectInputSubState::SUB_WAIT_POINTER_ACTION_BUTTON_UP; + SELECTION_HILOGI("set subSelectState to SUB_WAIT_POINTER_ACTION_BUTTON_UP."); + } else { + curSelectState = SelectInputState::SELECT_INPUT_WORD_BEGIN; + subSelectState = SelectInputSubState::SUB_INITIAL; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_WORD_BEGIN."); + } + lastClickTime = curTime; + return; + } + if (subSelectState == SelectInputSubState::SUB_WAIT_POINTER_ACTION_BUTTON_UP) { + if (action == PointerEvent::POINTER_ACTION_BUTTON_UP) { + if (ctrlSelectFlag) { + subSelectState = SelectInputSubState::SUB_WAIT_KEY_CTRL_DOWN; + SELECTION_HILOGI("set subSelectState to SUB_WAIT_KEY_CTRL_DOWN."); + } else { + curSelectState = SelectInputState::SELECT_INPUT_DOUBLE_CLICKED; + subSelectState = SelectInputSubState::SUB_INITIAL; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_DOUBLE_CLICKED."); + } + SaveSelectionEndInfo(pointerEvent); + } else if (action == PointerEvent::POINTER_ACTION_MOVE) { + curSelectState = SelectInputState::SELECT_INPUT_WAIT_LEFT_MOVE; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_WAIT_LEFT_MOVE."); + } + return; + } + if (subSelectState == SelectInputSubState::SUB_WAIT_KEY_CTRL_DOWN && + action == PointerEvent::POINTER_ACTION_BUTTON_DOWN) { + JudgeTripleClick(); + } + return; +} + +void BaseSelectionInputMonitor::InputWordJudgeTripleClickProcess(std::shared_ptr pointerEvent) const +{ + int32_t action = pointerEvent->GetPointerAction(); + if (subSelectState == SelectInputSubState::SUB_INITIAL && action == PointerEvent::POINTER_ACTION_BUTTON_DOWN) { + SELECTION_HILOGI("Begin JudgeTripleClick."); + JudgeTripleClick(); + } else { + SELECTION_HILOGI("Action reset. subSelectState is %{public}d, action is %{public}d.", subSelectState, action); + ResetProcess(pointerEvent); + } +} + +void BaseSelectionInputMonitor::InputWordWaitTripleClickProcess(std::shared_ptr pointerEvent) const +{ + int32_t action = pointerEvent->GetPointerAction(); + if (subSelectState != SelectInputSubState::SUB_WAIT_POINTER_ACTION_BUTTON_UP) { + return; + } + if (action == PointerEvent::POINTER_ACTION_BUTTON_UP) { + if (ctrlSelectFlag) { + subSelectState = SelectInputSubState::SUB_WAIT_KEY_CTRL_DOWN; + SELECTION_HILOGI("set subSelectState to SUB_WAIT_KEY_CTRL_DOWN."); + } else { + curSelectState = SelectInputState::SELECT_INPUT_TRIPLE_CLICKED; + subSelectState = SelectInputSubState::SUB_INITIAL; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_TRIPLE_CLICKED."); + } + SaveSelectionEndInfo(pointerEvent); + } else if (action == PointerEvent::POINTER_ACTION_MOVE) { + curSelectState = SelectInputState::SELECT_INPUT_WAIT_LEFT_MOVE; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_WAIT_LEFT_MOVE."); + } else { + SELECTION_HILOGI("Action reset. subSelectState is %{public}d, action is %{public}d.", subSelectState, action); + ResetProcess(pointerEvent); + } + return; +} + +void BaseSelectionInputMonitor::FinishedWordSelection() const +{ + if (!IsSelectionDone()) { + return; + } + GenerateSequenceId(); + SELECTION_HILOGI("[selectevent] curSelectState:%{public}d. Selection event id is %{public}u.", curSelectState, + selSeqId.load()); + isTextSelected_ = true; + SaveSelectionType(); +} + +void BaseSelectionInputMonitor::ResetFinishedState() const +{ + if (curSelectState != SelectInputState::SELECT_INPUT_DOUBLE_CLICKED) { + curSelectState = SelectInputState::SELECT_INPUT_INITIAL; + SELECTION_HILOGI("set curSelectState to SELECT_INPUT_INITIAL"); + } + isTextSelected_ = false; +} + +void BaseSelectionInputMonitor::ResetState() const +{ + isTextSelected_ = false; + curSelectState = SelectInputState::SELECT_INPUT_INITIAL; + subSelectState = SelectInputSubState::SUB_INITIAL; + SELECTION_HILOGD("ResetFinishedState."); +} + +SelectionInputMonitor::SelectionInputMonitor() { + baseInputMonitor_ = std::make_shared(); + appBlacklist_ = { + "com.huawei.hmos.hishell", + "com.huawei.hmos.filemanager" + }; +} + +void SelectionInputMonitor::OnInputEvent(std::shared_ptr keyEvent) const +{ + baseInputMonitor_->OnInputEvent(keyEvent); + FinishedWordSelection(); +} + +void SelectionInputMonitor::OnInputEvent(std::shared_ptr pointerEvent) const +{ + HandleWindowFocused(pointerEvent); + baseInputMonitor_->OnInputEvent(pointerEvent); + FinishedWordSelection(); +} + +void SelectionInputMonitor::OnInputEvent(std::shared_ptr axisEvent) const +{ + baseInputMonitor_->OnInputEvent(axisEvent); +} + +void SelectionInputMonitor::HandleWindowFocused(std::shared_ptr pointerEvent) const +{ + int32_t action = pointerEvent->GetPointerAction(); + if (action != PointerEvent::POINTER_ACTION_BUTTON_DOWN) { + return; + } + auto windowId = pointerEvent->GetTargetWindowId(); + Rosen::FocusChangeInfo focusInfo; + Rosen::WindowManager::GetInstance().GetFocusWindowInfo(focusInfo); + auto windowType = static_cast(focusInfo.windowType_); + if (windowId != focusInfo.windowId_) { + SELECTION_HILOGI("Clicked window is not focused, focus-changed event will dispose selection panel later"); + return; + } + SelectionFocusChangeInfo focusChangeInfo(focusInfo.windowId_, focusInfo.displayId_, focusInfo.pid_, + focusInfo.uid_, windowType, true, FocusChangeSource::InputManager); + sptr listener = SelectionService::GetInstance()->GetListener(); + if (listener == nullptr) { + SELECTION_HILOGE("Selection listener is nullptr"); + return; + } + ErrCode errCode = listener->FocusChange(focusChangeInfo); + if (errCode != NO_ERROR) { + SELECTION_HILOGE("Failed to call ISelectionListener::FocusChange, error code: %{public}d.", errCode); + } +} + +bool SelectionInputMonitor::IsAppInBlacklist(const std::string& bundleName) const { + return std::find(appBlacklist_.begin(), appBlacklist_.end(), bundleName) != appBlacklist_.end(); +} + +void SelectionInputMonitor::FinishedWordSelection() const +{ + if (!baseInputMonitor_->IsTextSelected()) { + return; + } + baseInputMonitor_->ResetFinishedState(); + if (pasteboardObserver_ == nullptr) { + pasteboardObserver_ = sptr::MakeSptr(baseInputMonitor_); + } + + auto selectionInfo = baseInputMonitor_->GetSelectionInfo(); + if (IsAppInBlacklist(selectionInfo.bundleName)) { + SELECTION_HILOGW("The app [%{public}s] is in the blacklist, skip notifying selection info.", + selectionInfo.bundleName.c_str()); + return; + } + if (!MemSelectionConfig::GetInstance().GetEnable()) { + SELECTION_HILOGI("Selection switch is off, skip notifying selection info."); + return; + } + + int32_t ret = PasteboardClient::GetInstance()->SubscribeDisposableObserver(pasteboardObserver_, + selectionInfo.bundleName, DisposableType::PLAIN_TEXT, MAX_PASTERBOARD_TEXT_LENGTH * BYTES_PER_CHINESE_CHAR); + SELECTION_HILOGI("[selectevent] Call pasteboard interface. Selection event id is %{public}u. \ + Error code is %{public}d.", selSeqId.load(), ret); + if (ret != ERR_OK) { + SELECTION_HILOGE("Failed to SubscribeDisposableObserver, ret: %{public}d.", ret); + return; + } + + InjectCtrlC(); + SELECTION_HILOGI("[selectevent] Inject Ctrl+C. Selection event id is %{public}u.", selSeqId.load()); +} + +void SelectionInputMonitor::InjectCtrlC() const +{ + // 创建KeyEvent对象 + auto keyEvent1 = KeyEvent::Create(); + + // 设置Ctrl键按下 + keyEvent1->AddFlag(InputEvent::EVENT_FLAG_NO_INTERCEPT); + keyEvent1->SetKeyCode(KeyEvent::KEYCODE_CTRL_LEFT); + keyEvent1->SetKeyAction(KeyEvent::KEY_ACTION_DOWN); + KeyEvent::KeyItem item1; + item1.SetKeyCode(KeyEvent::KEYCODE_CTRL_LEFT); + item1.SetPressed(true); + keyEvent1->AddKeyItem(item1); + InputManager::GetInstance()->SimulateInputEvent(keyEvent1); + + // 设置C键按下 + auto keyEvent2 = KeyEvent::Create(); + keyEvent2->AddFlag(InputEvent::EVENT_FLAG_NO_INTERCEPT); + keyEvent2->SetKeyCode(KeyEvent::KEYCODE_C); + keyEvent2->SetKeyAction(KeyEvent::KEY_ACTION_DOWN); + KeyEvent::KeyItem item2; + item2.SetKeyCode(KeyEvent::KEYCODE_C); + item2.SetPressed(true); + keyEvent2->AddKeyItem(item1); + keyEvent2->AddKeyItem(item2); + InputManager::GetInstance()->SimulateInputEvent(keyEvent2); + + // 设置C键释放 + auto keyEvent3 = KeyEvent::Create(); + keyEvent3->AddFlag(InputEvent::EVENT_FLAG_NO_INTERCEPT); + keyEvent3->SetKeyCode(KeyEvent::KEYCODE_C); + keyEvent3->SetKeyAction(KeyEvent::KEY_ACTION_UP); + KeyEvent::KeyItem item3; + item3.SetKeyCode(KeyEvent::KEYCODE_C); + item3.SetPressed(false); + keyEvent3->AddKeyItem(item1); + keyEvent3->AddKeyItem(item3); + InputManager::GetInstance()->SimulateInputEvent(keyEvent3); + + // 设置Ctrl键释放 + auto keyEvent4 = KeyEvent::Create(); + keyEvent4->AddFlag(InputEvent::EVENT_FLAG_NO_INTERCEPT); + keyEvent4->SetKeyCode(KeyEvent::KEYCODE_CTRL_LEFT); + keyEvent4->SetKeyAction(KeyEvent::KEY_ACTION_UP); + KeyEvent::KeyItem item4; + item4.SetKeyCode(KeyEvent::KEYCODE_CTRL_LEFT); + item4.SetPressed(false); + keyEvent4->AddKeyItem(item4); + InputManager::GetInstance()->SimulateInputEvent(keyEvent4); +} + +void SelectionPasteboardDisposableObserver::OnTextReceived(const std::string &text, int32_t errCode) +{ + SELECTION_HILOGI("[selectevent] Pasteboard call sa. Selection event id is %{public}u.", selSeqId.load()); + SELECTION_HILOGI("Text received length: %{public}u, errCode: %{public}d.", text.length(), errCode); + if (errCode != 0) { + SELECTION_HILOGI("Error receiving text, errCode: %{public}d", errCode); + return; + } + if (!baseInputMonitor_) { + return; + } + + auto isAllWhitespace = std::all_of(text.begin(), text.end(), [](unsigned char c) { + return std::isspace(c); + }); + if (isAllWhitespace) { + SELECTION_HILOGI("Received empty text or all whitespaces."); + return; + } + + auto selectionInfo = baseInputMonitor_->GetSelectionInfo(); + selectionInfo.text = text; + SelectionInfoData infoData; + infoData.data = selectionInfo; + SELECTION_HILOGI("SelectionInfoData: %{public}s.", infoData.ToString().c_str()); + sptr listener = SelectionService::GetInstance()->GetListener(); + if (listener == nullptr) { + SELECTION_HILOGE("Selection listener is nullptr"); + return; + } + listener->OnSelectionChange(infoData); +} diff --git a/service/src/selection_service.cpp b/service/src/selection_service.cpp new file mode 100644 index 0000000000000000000000000000000000000000..872e8cc5ec61b2ecacece1625b379610cb98a953 --- /dev/null +++ b/service/src/selection_service.cpp @@ -0,0 +1,424 @@ +/* + * 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 "selection_service.h" + +#include +#include +#include + +#include "ability_manager_client.h" +#include "db_selection_config_repository.h" +#include "iremote_object.h" +#include "callback_handler.h" +#include "system_ability_definition.h" +#include "selection_errors.h" +#include "selection_log.h" +#include +#include "parameter.h" +#include "common_event_manager.h" +#include "selection_config_comparator.h" +#include "selection_input_monitor.h" +#include "selection_interface.h" +#include "if_system_ability_manager.h" +#include "iservice_registry.h" +#include "focus_monitor_manager.h" +#include "os_account_manager.h" +#include "sys_selection_config_repository.h" +#include "selection_app_validator.h" + +#define SELECTION_MAX_TRY_TIMES 50 +#define SELECTION_SLEEP_TIME 100 + +using namespace OHOS; +using namespace OHOS::SelectionFwk; +using namespace OHOS::AppExecFwk; +using namespace OHOS::MMI; +using namespace OHOS::EventFwk; + +const bool REGISTER_RESULT = SystemAbility::MakeAndRegisterAbility(SelectionService::GetInstance().GetRefPtr()); +sptr SelectionService::listener_ { nullptr }; + +void SelectionExtensionAbilityConnection::OnAbilityConnectDone( + const ElementName &element, const sptr &remoteObject, int resultCode) +{ + SELECTION_HILOGI("OnAbilityConnectDone, bundle = %{public}s, ability = %{public}s, resultCode = %{public}d", + element.GetBundleName().c_str(), element.GetAbilityName().c_str(), resultCode); +} +void SelectionExtensionAbilityConnection::OnAbilityDisconnectDone(const ElementName &element, int resultCode) +{ + SELECTION_HILOGI("OnAbilityDisconnectDone, bundle = %{public}s,ability = %{public}s, resultCode = %{public}d", + element.GetBundleName().c_str(), element.GetAbilityName().c_str(), resultCode); + auto disconnectAppInfo = element.GetBundleName() + "/" + element.GetAbilityName(); + auto curAppInfo = MemSelectionConfig::GetInstance().GetSelectionConfig().GetApplicationInfo(); + if (curAppInfo == disconnectAppInfo) { + auto ret = SelectionService::GetInstance()->ConnectNewExtAbility(element.GetBundleName(), + element.GetAbilityName()); + SELECTION_HILOGD("Reconnect extension ability ret = %{public}d", ret); + } +} + +sptr SelectionService::GetInstance() +{ + static sptr instance = new (std::nothrow) SelectionService(); + return instance; +} + +SelectionService::SelectionService() : SystemAbility(SELECTION_FWK_SA_ID, true) +{ + SELECTION_HILOGI("[SelectionService] SelectionService()"); +} + +SelectionService::SelectionService(int32_t saId, bool runOnCreate) : SystemAbility(saId, runOnCreate) +{ + SELECTION_HILOGI("[SelectionService] saID=%{public}d runOnCreate=%{public}d", saId, runOnCreate); +} + +SelectionService::~SelectionService() +{ + SELECTION_HILOGI("[~SelectionService]"); +} + +sptr SelectionService::GetListener() { + std::lock_guard lock(mutex_); + return listener_; +} + +ErrCode SelectionService::RegisterListener(const sptr& listener) +{ + if (!SelectionAppValidator::GetInstance().Validate()) { + return SESSION_UNAUTHENTICATED_ERR; + } + + pid_.store(IPCSkeleton::GetCallingPid()); + SELECTION_HILOGI("Enter RegisterListener"); + if (listener == nullptr) { + SELECTION_HILOGE("RegisterListener: selection listener is nullptr."); + return ERR_INVALID_DATA; + } + + { + std::lock_guard lock(mutex_); + listener_ = listener; + } + + return 0; +} + +ErrCode SelectionService::UnregisterListener(const sptr& listener) +{ + std::lock_guard lock(mutex_); + listener_ = nullptr; + return 0; +} + +ErrCode SelectionService::IsCurrentSelectionApp(int pid, bool &resultValue) +{ + resultValue = (pid_.load() != -1 && pid == pid_.load()); + SELECTION_HILOGI("Checking IsCurrentSelectionApp: %{public}d", resultValue); + return 0; +} + +int32_t SelectionService::Dump(int32_t fd, const std::vector &args) +{ + dprintf(fd, "---------------------SelectionService::Dump--------------------\n"); + return OHOS::NO_ERROR; +} + +static void WatchEnableSwitch(const char *key, const char *value, void *context) +{ + SelectionService *selectionService = static_cast(context); + SELECTION_HILOGI("%{public}s: value=[%{public}s], DEFAULT_SWITCH =[%{public}s]", key, value, DEFAULT_SWITCH); + bool isEnabledValue = (strcmp(value, DEFAULT_SWITCH) == 0); + SELECTION_HILOGI("isEnabledValue is %{public}d", isEnabledValue); + MemSelectionConfig::GetInstance().SetEnabled(isEnabledValue); + + selectionService->PersistSelectionConfig(); +} + +static void WatchTriggerMode(const char *key, const char *value, void *context) +{ + SelectionService *selectionService = static_cast(context); + SELECTION_HILOGI("WatchTriggerMode begin"); + SELECTION_HILOGI("%{public}s: value=%{public}s", key, value); + int triggerCmpResult = strcmp(value, DEFAULT_TRIGGER); + BaseSelectionInputMonitor::ctrlSelectFlag = (triggerCmpResult == 0); + SELECTION_HILOGI("ctrlSelectFlag is %{public}d", BaseSelectionInputMonitor::ctrlSelectFlag); + + bool triggerValue = (triggerCmpResult == 0); + MemSelectionConfig::GetInstance().SetTriggered(triggerValue); + + selectionService->PersistSelectionConfig(); +} + +static void WatchAppSwitch(const char *key, const char *value, void *context) +{ + SELECTION_HILOGD("WatchAppSwitch begin"); + SELECTION_HILOGD("%{public}s: value=%{public}s", key, value); + SelectionService *selectionService = static_cast(context); + if (selectionService == nullptr) { + SELECTION_HILOGE("selectionService is nullptr"); + return; + } + + const std::string appInfo = value; + char target = '/'; + int count = std::count(appInfo.begin(), appInfo.end(), target); + if (count != 1) { + SELECTION_HILOGE("/ is not only one."); + return; + } + auto pos = appInfo.find('/'); + if (appInfo.empty() || pos == std::string::npos || pos + 1 >= appInfo.size()) { + SELECTION_HILOGE("app info: %{public}s is invalid!", appInfo.c_str()); + return; + } + const std::string bundleName = appInfo.substr(0, pos); + const std::string extName = appInfo.substr(pos + 1); + if (bundleName.length() == 0 || extName.length() == 0) { + SELECTION_HILOGE("bundleName or extName is empty"); + return; + } + MemSelectionConfig::GetInstance().SetApplicationInfo(appInfo); + selectionService->DisconnectCurrentExtAbility(); + auto ret = selectionService->ConnectNewExtAbility(bundleName, extName); + SELECTION_HILOGD("StartExtensionAbility ret = %{public}d", ret); + + selectionService->PersistSelectionConfig(); +} + +void SelectionService::PersistSelectionConfig() +{ + if (!IsUserLoggedIn()) { + SELECTION_HILOGW("Do not save selection config to DB because user is not logged in."); + return; + } + auto &selectionConfig = MemSelectionConfig::GetInstance().GetSelectionConfig(); + int ret = DbSelectionConfigRepository::GetInstance()->Save(GetUserId(), selectionConfig); + if (ret != SELECTION_CONFIG_OK) { + SELECTION_HILOGE("Add database failed. ret = %{public}d", ret); + } +} + +void SelectionService::DisconnectCurrentExtAbility() +{ + pid_.store(-1); + SELECTION_HILOGD("Disconnect current extensionAbility"); + std::lock_guard lockGuard(connectInnerMutex_); + if (connectInner_ == nullptr) { + SELECTION_HILOGE("connectInner_ is null"); + return; + } + + int32_t ret = AAFwk::AbilityManagerClient::GetInstance()->DisconnectAbility(connectInner_); + if (ret != ERR_OK) { + SELECTION_HILOGE("DisconnectServiceAbility failed, ret: %{public}d", ret); + return; + } + connectInner_ = nullptr; +} + +int32_t SelectionService::ConnectNewExtAbility( const std::string& bundleName, const std::string& abilityName) +{ + SELECTION_HILOGD("Start new SelectionExtension, bundleName:%{public}s, abilityName:%{public}s", bundleName.c_str(), + abilityName.c_str()); + AAFwk::Want want; + want.SetElementName(bundleName, abilityName); + + std::lock_guard lockGuard(connectInnerMutex_); + connectInner_ = sptr::MakeSptr(); + if (connectInner_ == nullptr) { + SELECTION_HILOGE("new(std::nothrow) SelectionExtensionAbilityConnection() failed!"); + return SELECTION_CONFIG_FAILURE; + } + auto ret = AAFwk::AbilityManagerClient::GetInstance()->ConnectAbility(want, connectInner_, -1); + if (ret != 0) { + SELECTION_HILOGE("[selectevent] StartExtensionAbility failed. error code is %{public}d.", ret); + return ret; + } + SELECTION_HILOGI("[selectevent] StartExtensionAbility success."); + return 0; +} + +void SelectionService::WatchParams() +{ + SELECTION_HILOGI("WatchParams begin"); + WatchParameter(SYS_SELECTION_SWITCH, WatchEnableSwitch, this); + WatchParameter(SYS_SELECTION_TRIGGER, WatchTriggerMode, this); + WatchParameter(SYS_SELECTION_APP, WatchAppSwitch, this); + SELECTION_HILOGI("WatchParams end"); +} + +int SelectionService::GetUserId() +{ + return userId_.load(); +} + +int SelectionService::LoadAccountLocalId() +{ + int32_t userId = -1; + int32_t ret = AccountSA::OsAccountManager::GetForegroundOsAccountLocalId(userId); + auto iCounter = 0; + while (ret != 0 && iCounter < SELECTION_MAX_TRY_TIMES) { + iCounter++; + std::this_thread::sleep_for(std::chrono::milliseconds(SELECTION_SLEEP_TIME)); + ret = AccountSA::OsAccountManager::GetForegroundOsAccountLocalId(userId); + } + SELECTION_HILOGI("GetForegroundOsAccountLocalId userId."); + userId_.store(userId); + return userId; +} + +bool SelectionService::IsUserLoggedIn() +{ + return LoadAccountLocalId() != -1; +} + +void SelectionService::SynchronizeSelectionConfig() +{ + if (!IsUserLoggedIn()) { + SELECTION_HILOGW("No selection config sync because user is not logged in."); + return; + } + SelectionConfig sysSelectionConfig = SysSelectionConfigRepository::GetInstance()->GetSysParameters(); + SELECTION_HILOGI("sysSelectionConfig: enable=%{public}d trigger=%{public}d applicationInfo=%{public}s", + sysSelectionConfig.GetEnable(), sysSelectionConfig.GetTriggered(), + sysSelectionConfig.GetApplicationInfo().c_str()); + auto dbSelectionConfig = DbSelectionConfigRepository::GetInstance()->GetOneByUserId(userId_.load()); + auto result = SelectionConfigComparator::Compare(userId_.load(), sysSelectionConfig, dbSelectionConfig); + MemSelectionConfig::GetInstance().SetSelectionConfig(result.selectionConfig); + + if (result.shouldCreate) { + SELECTION_HILOGI("result.shouldCreate"); + auto ret = DbSelectionConfigRepository::GetInstance()->Save(userId_.load(), result.selectionConfig); + if (ret != SELECTION_CONFIG_OK) { + SELECTION_HILOGE("Add database failed. ret = %{public}d", ret); + } + SELECTION_HILOGI("result.selectionConfig.isEnable = %{public}d", result.selectionConfig.GetEnable()); + SysSelectionConfigRepository::GetInstance()->SetSysParameters(result.selectionConfig); + return; + } + + if (result.direction == SyncDirection::FromDbToSys) { + SELECTION_HILOGI("result.direction == SyncDirection::FromDbToSys"); + SELECTION_HILOGI("FromDbToSys result: %{public}s", result.ToString().c_str()); + SysSelectionConfigRepository::GetInstance()->SetSysParameters(result.selectionConfig); + } else if (result.direction == SyncDirection::FromSysToDb) { + SELECTION_HILOGI("result.direction == SyncDirection::FromSysToDb"); + auto ret = DbSelectionConfigRepository::GetInstance()->Save(userId_.load(), result.selectionConfig); + if (ret != SELECTION_CONFIG_OK) { + SELECTION_HILOGE("Add database failed. ret = %{public}d", ret); + } + } + + if (result.shouldStop) { + SELECTION_HILOGI("result.shouldStop"); + SysSelectionConfigRepository::GetInstance()->DisableSAService(); + } +} + +void SelectionService::OnStart() +{ + SELECTION_HILOGI("[selectevent][SelectionService][OnStart]begin"); + Publish(SelectionService::GetInstance()); + InputMonitorInit(); + SynchronizeSelectionConfig(); + WatchParams(); + InitFocusChangedMonitor(); + SELECTION_HILOGI("[selectevent][SelectionService][OnStart]end."); +} + +void SelectionService::OnStop() +{ + SELECTION_HILOGI("[selectevent][SelectionService][OnStop]begin"); + InputMonitorCancel(); + CancelFocusChangedMonitor(); + SELECTION_HILOGI("[selectevent][SelectionService][OnStop]end."); +} + +void SelectionService::InputMonitorInit() +{ + SELECTION_HILOGI("[SelectionService] input monitor init"); + std::shared_ptr inputMonitor = std::make_shared(); + if (inputMonitorId_ > 0) { + return; + } + + auto sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + auto remoteObj = sam->CheckSystemAbility(MULTIMODAL_INPUT_SERVICE_ID); + auto iCounter = 0; + while (remoteObj == nullptr && iCounter < SELECTION_MAX_TRY_TIMES) { + SELECTION_HILOGI("CheckSystemAbility MULTIMODAL_INPUT_SERVICE_ID failed. wait..."); + iCounter++; + sleep(1); + remoteObj = sam->CheckSystemAbility(MULTIMODAL_INPUT_SERVICE_ID); + } + + if (remoteObj == nullptr) { + SELECTION_HILOGE("CheckSystemAbility MULTIMODAL_INPUT_SERVICE_ID failed."); + return; + } + + SELECTION_HILOGI("CheckSystemAbility MULTIMODAL_INPUT_SERVICE_ID succeed."); + inputMonitorId_ = InputManager::GetInstance()->AddMonitor(inputMonitor); + SELECTION_HILOGI("[SelectionService] input monitor init end"); +} + +void SelectionService::InputMonitorCancel() +{ + SELECTION_HILOGI("[SelectionService] input monitor cancel"); + InputManager* inputManager = InputManager::GetInstance(); + if (inputMonitorId_ >= 0) { + inputManager->RemoveMonitor(inputMonitorId_); + inputMonitorId_ = -1; + } +} + +void SelectionService::InitFocusChangedMonitor() +{ + SELECTION_HILOGI("[SelectionService] init focus changed monitor"); + FocusMonitorManager::GetInstance().RegisterFocusChangedListener( + [this](const sptr &focusChangeInfo, bool isFocused) { + HandleFocusChanged(focusChangeInfo, isFocused); + }); +} + +void SelectionService::CancelFocusChangedMonitor() +{ + SELECTION_HILOGI("[SelectionService] cancel focus changed monitor"); + FocusMonitorManager::GetInstance().UnregisterFocusChangedListener(); +} + +void SelectionService::HandleFocusChanged(const sptr &focusChangeInfo, bool isFocused) +{ + SELECTION_HILOGI("[SelectionService] handle focus changed"); + std::lock_guard lock(mutex_); + if (listener_ == nullptr || focusChangeInfo == nullptr) { + SELECTION_HILOGE("listener_ or focusChangeInfo is nullptr."); + return; + } + auto windowType = static_cast(focusChangeInfo->windowType_); + SelectionFocusChangeInfo selectionFocusChangeInfo(focusChangeInfo->windowId_, focusChangeInfo->displayId_, + focusChangeInfo->pid_, focusChangeInfo->uid_, windowType, isFocused, FocusChangeSource::WindowManager); + listener_->FocusChange(selectionFocusChangeInfo); +} + +void SelectionService::HandleKeyEvent(int32_t keyCode) +{ +} + +void SelectionService::HandlePointEvent(int32_t type) +{ +} \ No newline at end of file diff --git a/service/src/sys_selection_config_repository.cpp b/service/src/sys_selection_config_repository.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b7f3fcab93ca1553704abbbb6863f27d8f7cc07 --- /dev/null +++ b/service/src/sys_selection_config_repository.cpp @@ -0,0 +1,162 @@ +/* + * 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 "sys_selection_config_repository.h" +#include +#include "parameter.h" +#include "param_wrapper.h" +#include "selection_log.h" +#include "selection_common.h" + +namespace OHOS { +namespace SelectionFwk { +static const char *SELECTION_SWITCH = "sys.selection.switch"; +static const char *SELECTION_TRIGGER = "sys.selection.trigger"; +static const char *SELECTION_APPLICATION = "sys.selection.app"; +static const char *SELECTION_UID = "sys.selection.uid"; +static const int BUFFER_LEN = 200; + +#define SELECTION_MAX_UID_LENGTH 11 + +std::shared_ptr SysSelectionConfigRepository::instance_ = nullptr; + +std::shared_ptr SysSelectionConfigRepository::GetInstance() +{ + static std::mutex instanceMutex; + std::lock_guard guard(instanceMutex); + if (instance_ == nullptr) { + SELECTION_HILOGI("reset to new SysSelectionConfigRepository instance"); + instance_ = std::make_shared(); + } + return instance_; +} + +int SysSelectionConfigRepository::SetSysParameters(const SelectionConfig &info) +{ + SetEnabled(info.GetEnable()); + SetTriggered(info.GetTriggered()); + SetApplicationInfo(info.GetApplicationInfo()); + SetUid(info.GetUid()); + return 0; +} + +SelectionConfig SysSelectionConfigRepository::GetSysParameters() +{ + SelectionConfig info; + info.SetEnabled(GetEnable()); + info.SetTriggered(GetTriggered()); + info.SetApplicationInfo(GetApplicationInfo()); + info.SetUid(GetUid()); + return info; +} + +void SysSelectionConfigRepository::DisableSAService() +{ + SetParameter(SELECTION_SWITCH, "off"); +} + +int SysSelectionConfigRepository::GetEnable() +{ + char value[BUFFER_LEN]; + GetParameter(SELECTION_SWITCH, "", value, BUFFER_LEN); + if (strcmp(value, "on") == 0) { + return 1; + } + return 0; +} + +int SysSelectionConfigRepository::GetTriggered() +{ + char value[BUFFER_LEN]; + GetParameter(SELECTION_TRIGGER, "", value, BUFFER_LEN); + if (strcmp(value, "ctrl") == 0) { + return 1; + } + return 0; +} + +int SysSelectionConfigRepository::GetUid() +{ + std::string uidStr; + if (OHOS::system::GetStringParameter(SELECTION_UID, uidStr) != 0) { + SELECTION_HILOGE("GetStringParameter failed for SELECTION_UID"); + return -1; + } + + if (!isNumber(uidStr)) { + SELECTION_HILOGE("uidStr maybe not all of digit!"); + return -1; + } + + size_t maxLen = (uidStr[0] == '-') ? SELECTION_MAX_UID_LENGTH : SELECTION_MAX_UID_LENGTH - 1; + + if (uidStr.length() > maxLen) { + SELECTION_HILOGE("uidStr exceeds the range of int!"); + return -1; + } + + if ((uidStr.length() == maxLen) && + ((uidStr[0] != '-' && uidStr > "2147483647") || + (uidStr[0] == '-' && uidStr > "-2147483648"))) { + SELECTION_HILOGE("uidStr exceeds the range of int!"); + return -1; + } + + int uid = std::stoi(uidStr); + return uid; +} + +std::string SysSelectionConfigRepository::GetApplicationInfo() +{ + std::string appinfo; + if (OHOS::system::GetStringParameter(SELECTION_APPLICATION, appinfo) != 0) { + SELECTION_HILOGE("GetStringParameter failed for SELECTION_APPLICATION"); + return ""; + } + + return appinfo; +} + +void SysSelectionConfigRepository::SetEnabled(bool enabled) +{ + SELECTION_HILOGI("enabled: %{public}d", enabled); + if (enabled) { + SetParameter(SELECTION_SWITCH, "on"); + } else { + SetParameter(SELECTION_SWITCH, "off"); + } +} + +void SysSelectionConfigRepository::SetTriggered(bool isTriggered) +{ + if (isTriggered) { + SetParameter(SELECTION_TRIGGER, "ctrl"); + } else { + SetParameter(SELECTION_TRIGGER, ""); + } +} + +void SysSelectionConfigRepository::SetUid(int uid) +{ + std::string uidStr = std::to_string(uid); + SetParameter(SELECTION_UID, uidStr.c_str()); +} + +void SysSelectionConfigRepository::SetApplicationInfo(const std::string &applicationInfo) +{ + SetParameter(SELECTION_APPLICATION, applicationInfo.c_str()); +} +} // namespace SelectionFwk +} // namespace OHOS \ No newline at end of file diff --git a/test/fuzztest/BUILD.gn b/test/fuzztest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..a6e951d58425a78ec843b5165033086619e6e83c --- /dev/null +++ b/test/fuzztest/BUILD.gn @@ -0,0 +1,23 @@ +# 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("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +group("selection_service_fuzztest") { + testonly = true + + deps = [ + "selectioninputability_fuzzer:fuzztest", + "selectioninputlistener_fuzzer:listenerfuzztest", + ] +} diff --git a/test/fuzztest/selectioninputability_fuzzer/BUILD.gn b/test/fuzztest/selectioninputability_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..2e5b4971be3a2715de38633d77c80a27b29503a0 --- /dev/null +++ b/test/fuzztest/selectioninputability_fuzzer/BUILD.gn @@ -0,0 +1,69 @@ +# 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("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +#####################hydra-fuzz################### +import("//build/config/features.gni") +import("//build/ohos.gni") +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("SelectionInputAbilityFuzzTest") { + module_out_path = "selectionfwk/selectionfwk" + + fuzz_config_file = + "//foundation/systemabilitymgr/selectionfwk/test/fuzztest/selectioninputability_fuzzer" + + include_dirs = [ + "${selection_fwk_root_path}/frameworks/native/selection_ability/include", + "${selection_fwk_root_path}/utils/include", + "${target_gen_dir}", + "${selection_fwk_root_path}/common", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + sources = [ "selectioninputability_fuzzer.cpp" ] + + deps = [ + "${selection_fwk_root_path}/frameworks/native/selection_ability:selection_ability", + "${selection_fwk_root_path}/frameworks/native/selection_extension:selection_extension_ability_native", + "${selection_fwk_root_path}/service:selection_service", + ] + + external_deps = [ + "ability_runtime:ability_manager", + "ability_runtime:runtime", + "c_utils:utils", + "hilog:libhilog", + "input:libmmi-client", + "ipc:ipc_single", + "napi:ace_napi", + "window_manager:libdm", + "samgr:samgr_proxy", + ] +} + +############################################################################### +group("fuzztest") { + testonly = true + deps = [] + deps += [ ":SelectionInputAbilityFuzzTest" ] +} +############################################################################### diff --git a/test/fuzztest/selectioninputability_fuzzer/corpus/init b/test/fuzztest/selectioninputability_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..65af8ee8d11bf23407ea34d4de49f7cbb6a2b791 --- /dev/null +++ b/test/fuzztest/selectioninputability_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# 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. + +FUZZ \ No newline at end of file diff --git a/test/fuzztest/selectioninputability_fuzzer/project.xml b/test/fuzztest/selectioninputability_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..66e1dcac475475fb101b6f8670ec699e6e9696aa --- /dev/null +++ b/test/fuzztest/selectioninputability_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + diff --git a/test/fuzztest/selectioninputability_fuzzer/selectioninputability_fuzzer.cpp b/test/fuzztest/selectioninputability_fuzzer/selectioninputability_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc9f5c8ed120b5177a1af1e0a85261a1404526aa --- /dev/null +++ b/test/fuzztest/selectioninputability_fuzzer/selectioninputability_fuzzer.cpp @@ -0,0 +1,73 @@ +/* + * 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 "selectioninputability_fuzzer.h" +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include +#include "selection_log.h" +#include +#include +#include + +namespace OHOS { +namespace SelectionFwk { + +sptr GetSelectionSystemAbility() +{ + auto systemAbilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (systemAbilityManager == nullptr) { + SELECTION_HILOGE("system ability manager is nullptr!"); + return nullptr; + } + + sptr systemAbility = nullptr; + systemAbility = systemAbilityManager->GetSystemAbility(SELECTION_FWK_SA_ID); + if (systemAbility == nullptr) { + SELECTION_HILOGE("get system ability is nullptr!"); + return nullptr; + } + + return systemAbility; +} + +void TestSendRequest(uint32_t code) +{ + sptr systemAbility = GetSelectionSystemAbility(); + if (systemAbility == nullptr) { + SELECTION_HILOGE("get system ability is nullptr!"); + return; + } + MessageParcel data; + MessageParcel reply; + MessageOption option; + systemAbility->SendRequest(code, data, reply, option); +} + +} // namespace SelectionFwk +} // namespace OHOS + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (data == nullptr || size < sizeof(uint32_t)) { + return 0; + } + + auto fuzzedCode = static_cast(size); + + OHOS::SelectionFwk::TestSendRequest(fuzzedCode); + return 0; +} \ No newline at end of file diff --git a/test/fuzztest/selectioninputability_fuzzer/selectioninputability_fuzzer.h b/test/fuzztest/selectioninputability_fuzzer/selectioninputability_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..008250d13ea1202839d453b6b817629b1298cdf1 --- /dev/null +++ b/test/fuzztest/selectioninputability_fuzzer/selectioninputability_fuzzer.h @@ -0,0 +1,19 @@ +/* + * 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 SELECTION_INPUT_ABILITY_FUZZER +#define SELECTION_INPUT_ABILITY_FUZZER +#define FUZZ_PROJECT_NAME "selectioninputability_fuzzer" + +#endif // SELECTION_INPUT_ABILITY_FUZZER \ No newline at end of file diff --git a/test/fuzztest/selectioninputlistener_fuzzer/BUILD.gn b/test/fuzztest/selectioninputlistener_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..258850ea8194b1f64546c352883e6264a872b477 --- /dev/null +++ b/test/fuzztest/selectioninputlistener_fuzzer/BUILD.gn @@ -0,0 +1,70 @@ +# 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("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +import("//build/config/features.gni") +import("//build/ohos.gni") +import("//build/test.gni") + +ohos_fuzztest("SelectionInputListenerFuzzTest") { + module_out_path = "selectionfwk/selectionfwk" + + fuzz_config_file = + "//foundation/systemabilitymgr/selectionfwk/test/fuzztest/selectioninputlistener_fuzzer" + + include_dirs = [ + "${selection_fwk_root_path}/frameworks/native/selection_ability/include", + "${selection_fwk_root_path}/utils/include", + "${target_gen_dir}", + "${selection_fwk_root_path}/common", + "${selection_fwk_root_path}/service/include", + ] + + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + + sources = [ "selectioninputlistener_fuzzer.cpp" ] + + deps = [ + "${selection_fwk_root_path}/service:selection_service", + "${selection_fwk_root_path}/interfaces/idl:selection_service_interface", + "${selection_fwk_root_path}/common:selection_common", + "${selection_fwk_root_path}/interfaces/idl:selection_service_proxy", + "${selection_fwk_root_path}/interfaces/idl:selection_listener_proxy", + ] + + external_deps = [ + "ability_runtime:ability_manager", + "ability_runtime:runtime", + "c_utils:utils", + "hilog:libhilog", + "input:libmmi-client", + "ipc:ipc_single", + "napi:ace_napi", + "window_manager:libdm", + "samgr:samgr_proxy", + "window_manager:libdm_lite", + "window_manager:libwsutils", + ] +} + +group("listenerfuzztest") { + testonly = true + deps = [] + deps += [ ":SelectionInputListenerFuzzTest" ] +} diff --git a/test/fuzztest/selectioninputlistener_fuzzer/corpus/init b/test/fuzztest/selectioninputlistener_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..65af8ee8d11bf23407ea34d4de49f7cbb6a2b791 --- /dev/null +++ b/test/fuzztest/selectioninputlistener_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# 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. + +FUZZ \ No newline at end of file diff --git a/test/fuzztest/selectioninputlistener_fuzzer/project.xml b/test/fuzztest/selectioninputlistener_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..66e1dcac475475fb101b6f8670ec699e6e9696aa --- /dev/null +++ b/test/fuzztest/selectioninputlistener_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + diff --git a/test/fuzztest/selectioninputlistener_fuzzer/selectioninputlistener_fuzzer.cpp b/test/fuzztest/selectioninputlistener_fuzzer/selectioninputlistener_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..21d8feacefb118bc7ddeb426f2d93f72f7eeaf0e --- /dev/null +++ b/test/fuzztest/selectioninputlistener_fuzzer/selectioninputlistener_fuzzer.cpp @@ -0,0 +1,107 @@ +/* + * 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 "selectioninputlistener_fuzzer.h" +#include "system_ability_definition.h" +#include "selection_data_inner.h" +#include "iselection_listener.h" +#include +#include "selection_log.h" +#include "iservice_registry.h" +#include +#include +#include + +namespace OHOS { +namespace SelectionFwk { +sptr GetSelectionSystemAbility() +{ + auto systemAbilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (systemAbilityManager == nullptr) { + SELECTION_HILOGE("system ability manager is nullptr!"); + return nullptr; + } + + sptr systemAbility = nullptr; + systemAbility = systemAbilityManager->GetSystemAbility(SELECTION_FWK_SA_ID); + if (systemAbility == nullptr) { + SELECTION_HILOGE("get system ability is nullptr!"); + return nullptr; + } + + return systemAbility; +} + +void TestOnSelectionChange(std::string fuzzedString, int32_t fuzzedInt32, uint32_t fuzzedUInt32) +{ + auto remote = GetSelectionSystemAbility(); + auto listener = iface_cast(remote); + + if (listener == nullptr) { + SELECTION_HILOGE("get listener is null"); + return; + } + + SelectionInfoData dataInner; + dataInner.data.selectionType = MOVE_SELECTION; + dataInner.data.text = fuzzedString; + dataInner.data.startDisplayX = fuzzedInt32; + dataInner.data.startDisplayY = fuzzedInt32; + dataInner.data.endDisplayX = fuzzedInt32; + dataInner.data.endDisplayY = fuzzedInt32; + dataInner.data.startWindowX = fuzzedInt32; + dataInner.data.startWindowY = fuzzedInt32; + dataInner.data.endWindowX = fuzzedInt32; + dataInner.data.endWindowY = fuzzedInt32; + dataInner.data.displayId = fuzzedUInt32; + dataInner.data.windowId = fuzzedUInt32; + + listener->OnSelectionChange(dataInner); +} + +void TestFocusChange(uint32_t fuzzedUInt32) +{ + auto remote = GetSelectionSystemAbility(); + auto listener = iface_cast(remote); + + if (listener == nullptr) { + SELECTION_HILOGE("get listener is null"); + return; + } + SelectionFocusChangeInfo selectionFocusChangeInfo; + selectionFocusChangeInfo.windowId_ = fuzzedUInt32; + selectionFocusChangeInfo.windowType_ = fuzzedUInt32; + + listener->FocusChange(selectionFocusChangeInfo); +} +} // namespace SelectionFwk +} // namespace OHOS + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (data == nullptr || size < sizeof(uint32_t)) { + return 0; + } + + std::string fuzzedString(reinterpret_cast(data), size); + auto fuzzedUInt32 = static_cast(size); + auto fuzzedInt32 = static_cast(size); + + OHOS::SelectionFwk::TestOnSelectionChange(fuzzedString, fuzzedInt32, fuzzedUInt32); + OHOS::SelectionFwk::TestFocusChange(fuzzedUInt32); + + return 0; +} \ No newline at end of file diff --git a/test/fuzztest/selectioninputlistener_fuzzer/selectioninputlistener_fuzzer.h b/test/fuzztest/selectioninputlistener_fuzzer/selectioninputlistener_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..02b391d6aed00b4cf510da285d80fca871d16c62 --- /dev/null +++ b/test/fuzztest/selectioninputlistener_fuzzer/selectioninputlistener_fuzzer.h @@ -0,0 +1,19 @@ +/* + * 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 SELECTION_INPUT_LISTENER_FUZZER +#define SELECTION_INPUT_LISTENER_FUZZER +#define FUZZ_PROJECT_NAME "selectioninputlistener_fuzzer" + +#endif // SELECTION_INPUT_LISTENER_FUZZER \ No newline at end of file diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..f442e198a6825fee817a74f81dcf3152c68b8c99 --- /dev/null +++ b/test/unittest/BUILD.gn @@ -0,0 +1,62 @@ +# Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development 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/test.gni") +import("//foundation/systemabilitymgr/selectionfwk/selection_service.gni") + +module_out_path = "selectionfwk/selectionfwk" +ohos_unittest("selection_service_unit_test") { + module_out_path = module_out_path + cflags_cc = [ "-std=c++17" ] + include_dirs = [ + "./", + "${selection_fwk_root_path}/service/include", + "${selection_fwk_root_path}/utils/include", + ] + + sources = [ + "selection_config_comparator_test.cpp", + "selection_input_monitor_test.cpp", + ] + deps = [ + "${selection_fwk_root_path}/service:selection_service", + "${selection_fwk_root_path}/common:selection_common", + ] + external_deps = [ + "ability_base:want", + "ability_runtime:ability_manager", + "c_utils:utils", + "googletest:gmock_main", + "googletest:gtest_main", + "napi:ace_napi", + "hilog:libhilog", + "ipc:ipc_single", + "init:libbeget_proxy", + "input:libmmi-client", + "pasteboard:pasteboard_client", + "safwk:system_ability_fwk", + "samgr:samgr_proxy", + ] + part_name = "selectionfwk" + subsystem_name = "systemabilitymgr" +} + +group("selection_manager_ut") { + testonly = true + deps = [ + ":selection_service_unit_test", + "resource/ohos_test:copy_ohos_test", + "selection_manager_js_test:SelectionManagerJsTest", + "selection_manager_invalid_operation_js_test:SelectionManagerInvalidOperationJsTest", + ] +} \ No newline at end of file diff --git a/test/unittest/resource/ohos_test/BUILD.gn b/test/unittest/resource/ohos_test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..11b66e5dc796e7397b9e08e9af92672184a7315c --- /dev/null +++ b/test/unittest/resource/ohos_test/BUILD.gn @@ -0,0 +1,19 @@ +# 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/ohos.gni") + +ohos_copy("copy_ohos_test") { + sources = [ "./ohos_test.xml" ] + outputs = [ "$root_out_dir/tests/unittest/selectionfwk/selectionfwk/resource/ohos_test.xml" ] +} diff --git a/test/unittest/resource/ohos_test/ohos_test.xml b/test/unittest/resource/ohos_test/ohos_test.xml new file mode 100644 index 0000000000000000000000000000000000000000..b4801c58d8ae61d763adf633b39e55c32e1e5dd9 --- /dev/null +++ b/test/unittest/resource/ohos_test/ohos_test.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/test/unittest/selection_config_comparator_test.cpp b/test/unittest/selection_config_comparator_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2794a4758315df2366543e2973774b1b962d3b8d --- /dev/null +++ b/test/unittest/selection_config_comparator_test.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2022 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 + +#include "gtest/gtest.h" + +#define private public +#include "selection_config_comparator.h" +#include "selection_config.h" + +namespace OHOS { +namespace SelectionFwk { + +using namespace testing::ext; + +class SelectionConfigComparatorTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void SelectionConfigComparatorTest::SetUpTestCase() +{ + std::cout << "SelectionConfigComparatorTest SetUpTestCase" << std::endl; +} + +void SelectionConfigComparatorTest::TearDownTestCase() +{ + std::cout << "SelectionConfigComparatorTest TearDownTestCase" << std::endl; +} + +void SelectionConfigComparatorTest::SetUp() +{ + std::cout << "SelectionConfigComparatorTest SetUp" << std::endl; +} + +void SelectionConfigComparatorTest::TearDown() +{ + std::cout << "SelectionConfigComparatorTest TearDown" << std::endl; +} + +void SetSelectionConfig(SelectionConfig& dbSelectionConfig, int uid, bool bEnable) +{ + dbSelectionConfig.SetUid(uid); + dbSelectionConfig.SetEnabled(bEnable); +} + +/** + * @tc.name: SelectionConfigComparator001 + * @tc.desc: database testcase 001 + * @tc.type: FUNC + */ +HWTEST_F(SelectionConfigComparatorTest, SelectionConfigComparator001, TestSize.Level1) +{ + int uid = 100; + SelectionConfig sysSelectionConfig; + std::optional dbSelectionConfig = std::nullopt; + auto result = SelectionConfigComparator::Compare(uid, sysSelectionConfig, dbSelectionConfig); + ASSERT_TRUE(result.shouldCreate); +} + +/** + * @tc.name: SelectionConfigComparator002 + * @tc.desc: database testcase 002 + * @tc.type: FUNC + */ +HWTEST_F(SelectionConfigComparatorTest, SelectionConfigComparator002, TestSize.Level1) +{ + int uid = 100; + SelectionConfig dbSelectionConfig; + SetSelectionConfig(dbSelectionConfig, uid, true); + std::optional dbSelectionConfigOpt = dbSelectionConfig; + SelectionConfig sysSelectionConfig; + auto result = SelectionConfigComparator::Compare(uid, sysSelectionConfig, dbSelectionConfigOpt); + ASSERT_EQ(result.direction, SyncDirection::FromDbToSys); + ASSERT_EQ(result.selectionConfig.GetUid(), 100); + ASSERT_TRUE(result.selectionConfig.GetEnable()); +} + +/** + * @tc.name: SelectionConfigComparator003 + * @tc.desc: database testcase 003 + * @tc.type: FUNC + */ +HWTEST_F(SelectionConfigComparatorTest, SelectionConfigComparator003, TestSize.Level1) +{ + int uid = 100; + SelectionConfig dbSelectionConfig; + SetSelectionConfig(dbSelectionConfig, uid, true); + std::optional dbSelectionConfigOpt = dbSelectionConfig; + SelectionConfig sysSelectionConfig; + SetSelectionConfig(sysSelectionConfig, uid, false); + auto result = SelectionConfigComparator::Compare(uid, sysSelectionConfig, dbSelectionConfigOpt); + ASSERT_EQ(result.direction, SyncDirection::FromSysToDb); + ASSERT_TRUE(result.shouldStop); + ASSERT_FALSE(result.selectionConfig.GetEnable()); +} + +/** + * @tc.name: SelectionConfigComparator004 + * @tc.desc: database testcase 004 + * @tc.type: FUNC + */ +HWTEST_F(SelectionConfigComparatorTest, SelectionConfigComparator004, TestSize.Level1) +{ + int uid = 100; + SelectionConfig dbSelectionConfig; + SetSelectionConfig(dbSelectionConfig, uid, true); + std::optional dbSelectionConfigOpt = dbSelectionConfig; + SelectionConfig sysSelectionConfig; + SetSelectionConfig(sysSelectionConfig, uid, true); + auto result = SelectionConfigComparator::Compare(uid, sysSelectionConfig, dbSelectionConfigOpt); + ASSERT_EQ(result.direction, SyncDirection::FromSysToDb); +} + +/** + * @tc.name: SelectionConfigComparator005 + * @tc.desc: database testcase 005 + * @tc.type: FUNC + */ +HWTEST_F(SelectionConfigComparatorTest, SelectionConfigComparator005, TestSize.Level1) +{ + int uid = 100; + SelectionConfig dbSelectionConfig; + SetSelectionConfig(dbSelectionConfig, uid, false); + std::optional dbSelectionConfigOpt = dbSelectionConfig; + SelectionConfig sysSelectionConfig; + auto result = SelectionConfigComparator::Compare(uid, sysSelectionConfig, dbSelectionConfigOpt); + ASSERT_TRUE(result.shouldStop); + ASSERT_EQ(result.selectionConfig.GetUid(), 100); +} + +/** + * @tc.name: SelectionConfigComparator006 + * @tc.desc: database testcase 006 + * @tc.type: FUNC + */ +HWTEST_F(SelectionConfigComparatorTest, SelectionConfigComparator006, TestSize.Level1) +{ + int uid = 100; + SelectionConfig dbSelectionConfig; + SetSelectionConfig(dbSelectionConfig, uid, false); + std::optional dbSelectionConfigOpt = dbSelectionConfig; + SelectionConfig sysSelectionConfig; + SetSelectionConfig(sysSelectionConfig, uid, false); + auto result = SelectionConfigComparator::Compare(uid, sysSelectionConfig, dbSelectionConfigOpt); + ASSERT_EQ(result.direction, SyncDirection::FromSysToDb); + ASSERT_TRUE(result.shouldStop); +} + +/** + * @tc.name: SelectionConfigComparator007 + * @tc.desc: database testcase 007 + * @tc.type: FUNC + */ +HWTEST_F(SelectionConfigComparatorTest, SelectionConfigComparator007, TestSize.Level1) +{ + int uid = 100; + SelectionConfig dbSelectionConfig; + SetSelectionConfig(dbSelectionConfig, uid, false); + std::optional dbSelectionConfigOpt = dbSelectionConfig; + SelectionConfig sysSelectionConfig; + SetSelectionConfig(sysSelectionConfig, uid, true); + auto result = SelectionConfigComparator::Compare(uid, sysSelectionConfig, dbSelectionConfigOpt); + ASSERT_EQ(result.direction, SyncDirection::FromSysToDb); + ASSERT_TRUE(result.selectionConfig.GetEnable()); +} +} +} \ No newline at end of file diff --git a/test/unittest/selection_input_monitor_test.cpp b/test/unittest/selection_input_monitor_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59368eec8956ddea658a38ee4d71fa4076d5dbb7 --- /dev/null +++ b/test/unittest/selection_input_monitor_test.cpp @@ -0,0 +1,425 @@ +/* + * 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 "gtest/gtest.h" +#include +#include + +#include "selection_input_monitor.h" + +namespace OHOS { +namespace SelectionFwk { + +using namespace testing::ext; + +struct EventStruct { + int buttonId; + int action; +}; + +std::shared_ptr GetPointerEvent() +{ + std::shared_ptr pointEvent = PointerEvent::Create(); + PointerEvent::PointerItem pointerItem; + pointerItem.SetDisplayX(100); + pointerItem.SetDisplayY(200); + pointerItem.SetWindowX(50); + pointerItem.SetWindowY(60); + + pointEvent->AddPointerItem(pointerItem); + return pointEvent; +} + +template +void LEFT_BUTTON_DOWN(std::shared_ptr handler) +{ + std::shared_ptr pointEvent = GetPointerEvent(); + pointEvent->SetButtonId(PointerEvent::MOUSE_BUTTON_LEFT); + pointEvent->SetPointerAction(PointerEvent::POINTER_ACTION_BUTTON_DOWN); + handler->OnInputEvent(pointEvent); +} + +template +void LEFT_BUTTON_UP(std::shared_ptr handler) +{ + std::shared_ptr pointEvent = GetPointerEvent(); + pointEvent->SetButtonId(PointerEvent::MOUSE_BUTTON_LEFT); + pointEvent->SetPointerAction(PointerEvent::POINTER_ACTION_BUTTON_UP); + handler->OnInputEvent(pointEvent); +} + +template +void LEFT_BUTTON_CLICK(std::shared_ptr handler) +{ + LEFT_BUTTON_DOWN(handler); + LEFT_BUTTON_UP(handler); +} + +template +void RIGHT_BUTTON_DOWN(std::shared_ptr handler) +{ + std::shared_ptr pointEvent = GetPointerEvent(); + pointEvent->SetButtonId(PointerEvent::MOUSE_BUTTON_RIGHT); + pointEvent->SetPointerAction(PointerEvent::POINTER_ACTION_BUTTON_DOWN); + handler->OnInputEvent(pointEvent); +} + +template +void RIGHT_BUTTON_UP(std::shared_ptr handler) +{ + std::shared_ptr pointEvent = GetPointerEvent(); + pointEvent->SetButtonId(PointerEvent::MOUSE_BUTTON_RIGHT); + pointEvent->SetPointerAction(PointerEvent::POINTER_ACTION_BUTTON_UP); + handler->OnInputEvent(pointEvent); +} + +template +void LEFT_BUTTON_MOVE(std::shared_ptr handler) +{ + std::shared_ptr pointEvent = GetPointerEvent(); + pointEvent->SetButtonId(PointerEvent::MOUSE_BUTTON_LEFT); + pointEvent->SetPointerAction(PointerEvent::PointerEvent::POINTER_ACTION_MOVE); + handler->OnInputEvent(pointEvent); +} + +template +void CTRL_DOWN(std::shared_ptr handler) +{ + std::shared_ptr keyEvent = KeyEvent::Create(); + keyEvent->SetKeyCode(KeyEvent::KEYCODE_CTRL_LEFT); + keyEvent->SetKeyAction(KeyEvent::KEY_ACTION_DOWN); + handler->OnInputEvent(keyEvent); +} + +template +void CTRL_UP(std::shared_ptr handler) +{ + std::shared_ptr keyEvent = KeyEvent::Create(); + keyEvent->SetKeyCode(KeyEvent::KEYCODE_CTRL_LEFT); + keyEvent->SetKeyAction(KeyEvent::KEY_ACTION_UP); + handler->OnInputEvent(keyEvent); +} + +void WAIT_TIMEOUT() +{ + std::this_thread::sleep_for(std::chrono::milliseconds(DOUBLE_CLICK_TIME + 1)); // >500ms +} + +void CHECK_INFO(const SelectionInfo& info) +{ + EXPECT_NE(info.selectionType, 0); + EXPECT_GT(info.startDisplayX, 0); + EXPECT_GT(info.startDisplayY, 0); + EXPECT_GT(info.endDisplayX, 0); + EXPECT_GT(info.endDisplayY, 0); + EXPECT_GT(info.startWindowX, 0); + EXPECT_GT(info.startWindowY, 0); + EXPECT_GT(info.endWindowX, 0); + EXPECT_GT(info.endWindowY, 0); +} + +class BaseSelectionInputMonitorTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); + std::shared_ptr inputMonitor = nullptr; +}; + +void BaseSelectionInputMonitorTest::SetUpTestCase() +{ + std::cout << "BaseSelectionInputMonitorTest SetUpTestCase" << std::endl; +} + +void BaseSelectionInputMonitorTest::TearDownTestCase() +{ + std::cout << "BaseSelectionInputMonitorTest TearDownTestCase" << std::endl; +} + +void BaseSelectionInputMonitorTest::SetUp() +{ + std::cout << "BaseSelectionInputMonitorTest SetUp" << std::endl; + inputMonitor = std::make_shared(); +} + +void BaseSelectionInputMonitorTest::TearDown() +{ + std::cout << "BaseSelectionInputMonitorTest TearDown" << std::endl; +} + +/** + * @tc.name: param check samgr ready event + * @tc.desc: param check samgr ready event + * @tc.type: FUNC + */ +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor001, TestSize.Level1) +{ + std::cout << " SelectInputMonitor001 start " << std::endl; + BaseSelectionInputMonitor::ctrlSelectFlag = false; + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_MOVE(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor002, TestSize.Level1) +{ + std::cout << " SelectInputMonitor002 start " << std::endl; + LEFT_BUTTON_CLICK(inputMonitor); + LEFT_BUTTON_CLICK(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor003, TestSize.Level1) +{ + std::cout << " SelectInputMonitor003 start " << std::endl; + LEFT_BUTTON_CLICK(inputMonitor); + LEFT_BUTTON_CLICK(inputMonitor); + LEFT_BUTTON_CLICK(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor004, TestSize.Level1) +{ + std::cout << " SelectInputMonitor004 start " << std::endl; + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + RIGHT_BUTTON_DOWN(inputMonitor); + RIGHT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, false); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor005, TestSize.Level1) +{ + std::cout << " SelectInputMonitor005 start " << std::endl; + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + WAIT_TIMEOUT(); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, false); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor006, TestSize.Level1) +{ + std::cout << " SelectInputMonitor006 start " << std::endl; + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + WAIT_TIMEOUT(); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor007, TestSize.Level1) +{ + std::cout << " SelectInputMonitor007 start " << std::endl; + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + WAIT_TIMEOUT(); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor008, TestSize.Level1) +{ + std::cout << " SelectInputMonitor008 start " << std::endl; + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + WAIT_TIMEOUT(); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + WAIT_TIMEOUT(); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, false); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor009, TestSize.Level1) +{ + std::cout << " SelectInputMonitor009 start " << std::endl; + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_MOVE(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor010, TestSize.Level1) +{ + std::cout << " SelectInputMonitor010 start " << std::endl; + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_MOVE(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor011, TestSize.Level1) +{ + std::cout << " SelectInputMonitor011 start " << std::endl; + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_MOVE(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + CTRL_DOWN(inputMonitor); + CTRL_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor012, TestSize.Level1) +{ + std::cout << " SelectInputMonitor012 start " << std::endl; + CTRL_DOWN(inputMonitor); + CTRL_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_MOVE(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor013, TestSize.Level1) +{ + std::cout << " SelectInputMonitor013 start " << std::endl; + CTRL_DOWN(inputMonitor); + CTRL_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor014, TestSize.Level1) +{ + std::cout << " SelectInputMonitor014 start " << std::endl; + CTRL_DOWN(inputMonitor); + CTRL_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, true); + auto info = inputMonitor->GetSelectionInfo(); + CHECK_INFO(info); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor015, TestSize.Level1) +{ + std::cout << " SelectInputMonitor015 start " << std::endl; + CTRL_DOWN(inputMonitor); + CTRL_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, false); +} + +HWTEST_F(BaseSelectionInputMonitorTest, SelectInputMonitor016, TestSize.Level1) +{ + std::cout << " SelectInputMonitor016 start " << std::endl; + CTRL_DOWN(inputMonitor); + CTRL_UP(inputMonitor); + + LEFT_BUTTON_DOWN(inputMonitor); + LEFT_BUTTON_UP(inputMonitor); + + auto ret = inputMonitor->IsTextSelected(); + ASSERT_EQ(ret, false); +} + +} // namespace SelectionFwk +} // namespace OHOS diff --git a/test/unittest/selection_manager_invalid_operation_js_test/BUILD.gn b/test/unittest/selection_manager_invalid_operation_js_test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..40e0be085e4f2ac39b81f53b38c608b0c277e917 --- /dev/null +++ b/test/unittest/selection_manager_invalid_operation_js_test/BUILD.gn @@ -0,0 +1,21 @@ +# 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/test.gni") + +module_output_path = "selectionfwk/selectionfwk" +ohos_js_unittest("SelectionManagerInvalidOperationJsTest") { + module_out_path = module_output_path + hap_profile = "./config.json" + certificate_profile = "./openharmony_sx.p7b" +} diff --git a/test/unittest/selection_manager_invalid_operation_js_test/InvalidOperationTest.js b/test/unittest/selection_manager_invalid_operation_js_test/InvalidOperationTest.js new file mode 100644 index 0000000000000000000000000000000000000000..a9ce1ccf3b98dd1fba65eef51e1bf899e249e326 --- /dev/null +++ b/test/unittest/selection_manager_invalid_operation_js_test/InvalidOperationTest.js @@ -0,0 +1,76 @@ +/* + * 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 selectionManager from '@ohos.selectionInput.selectionManager' +import { describe, beforeAll, afterAll, it, expect } from 'deccjsunit/index' +import { PanelType } from '@ohos.selectionInput.SelectionPanel'; + +describe("SelectionManagerInvalidOperationJsTest", function () { + beforeAll(function () { + console.info('beforeAll called'); + }) + + afterAll(function () { + console.info('AfterAll called'); + }) + + /* + * @tc.number selectionfwk_on_selectionCompleted_invalid_operation_001 + * @tc.name Test should throw BusinessError 33600003 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_on_selectionCompleted_invalid_operation_001', 0, async function (done) { + console.info('************* selectionfwk_on_selectionCompleted_invalid_operation_001*************'); + try { + selectionManager.on('selectionCompleted', (info) => { + console.info(`selectionfwk_on_selectionCompleted_invalid_operation_001 is failed`); + expect(false).assertTrue(); + }) + expect(false).assertTrue(); + } catch(error) { + console.info(`selectionfwk_on_selectionCompleted_invalid_operation_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(33600003); + } + console.info('************* selectionfwk_on_selectionCompleted_invalid_operation_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_invalid_operation_001 + * @tc.name Test should throw BusinessError 33600003 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_invalid_operation_001', 0, async function (done) { + console.info('************* selectionfwk_createPanel_invalid_operation_001 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MENU_PANEL, + x: 0, + y: 0, + width: 100, + height: 100 + } + await selectionManager.createPanel({stageMode : false}, panelInfo) + expect(false).assertTrue(); + } catch (error) { + console.info(`selectionfwk_createPanel_invalid_operation_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(33600003); + } + console.info('************* selectionfwk_createPanel_invalid_operation_001 Test end*************'); + done(); + }); +}); diff --git a/test/unittest/selection_manager_invalid_operation_js_test/config.json b/test/unittest/selection_manager_invalid_operation_js_test/config.json new file mode 100644 index 0000000000000000000000000000000000000000..77f79bed206701c815c26a774fc969bb492dfa21 --- /dev/null +++ b/test/unittest/selection_manager_invalid_operation_js_test/config.json @@ -0,0 +1,63 @@ +{ + "app": { + "bundleName": "com.example.selectioninvalidoperationtest", + "vendor": "example", + "version": { + "code": 1, + "name": "1.0" + }, + "apiVersion": { + "compatible": 20, + "target": 20 + } + }, + "deviceConfig": {}, + "module": { + "reqPermissions": [], + "package": "com.example.selectioninvalidoperationtest", + "name": ".MyApplication", + "deviceType": [ + "default", + "tablet", + "2in1" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry" + }, + "abilities": [ + { + "visible": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "name": "com.example.selectioninvalidoperationtest.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "PermissionJsTest", + "type": "page", + "launchType": "standard" + } + ], + "js": [ + { + "pages": [ + "pages/index/index" + ], + "name": "default", + "window": { + "designWidth": 720, + "autoDesignWidth": false + } + } + ] + } +} diff --git a/test/unittest/selection_manager_invalid_operation_js_test/openharmony_sx.p7b b/test/unittest/selection_manager_invalid_operation_js_test/openharmony_sx.p7b new file mode 100644 index 0000000000000000000000000000000000000000..9af661cd0638e36d3f6ca6e3b65c8809b122fd19 Binary files /dev/null and b/test/unittest/selection_manager_invalid_operation_js_test/openharmony_sx.p7b differ diff --git a/test/unittest/selection_manager_js_test/BUILD.gn b/test/unittest/selection_manager_js_test/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..136931ea46aa79c60e1dbf1449fbac343cc08567 --- /dev/null +++ b/test/unittest/selection_manager_js_test/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright (c) 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. + +import("//build/test.gni") + +module_output_path = "selectionfwk/selectionfwk" +ohos_js_unittest("SelectionManagerJsTest") { + module_out_path = module_output_path + hap_profile = "./config.json" + certificate_profile = "./openharmony_sx.p7b" +} diff --git a/test/unittest/selection_manager_js_test/PermissionJsTest.js b/test/unittest/selection_manager_js_test/PermissionJsTest.js new file mode 100644 index 0000000000000000000000000000000000000000..534d821636f14c88ecbd44e4979552e6932f7359 --- /dev/null +++ b/test/unittest/selection_manager_js_test/PermissionJsTest.js @@ -0,0 +1,1865 @@ +/* + * 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 selectionManager from '@ohos.selectionInput.selectionManager' +import { describe, beforeAll, afterAll, beforeEach, afterEach, it, expect } from 'deccjsunit/index' +import { PanelType } from '@ohos.selectionInput.SelectionPanel'; + +describe("SelectionManagerJsTest", function () { + beforeAll(function () { + console.info('beforeAll called'); + }) + + afterAll(function () { + console.info('afterAll called'); + }) + + beforeEach(function () { + console.info('beforeEach called'); + }); + + afterEach(function () { + console.info('afterEach called'); + }); + + async function createSelectionPanel() + { + let panelInfo = { + panelType: PanelType.MENU_PANEL, + x: 0, + y: 0, + width: 100, + height: 100 + } + return await selectionManager.createPanel({stageMode : false}, panelInfo); + } + + async function destroySelectionPanel(panel) + { + await selectionManager.destroyPanel(panel); + } + + /* + * @tc.number selectionfwk_on_selectionCompleted_001 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_on_selectionCompleted_001', 0, async function (done) { + console.info('************* selectionfwk_on_selectionCompleted_001*************'); + try { + selectionManager.on('selectionCompletedtest', (info) => { + console.info(`selectionfwk_on_selectionCompleted_001 is failed`); + expect(false).assertTrue(); + done(); + }); + console.info(`selectionfwk_on_selectionCompleted_001 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_on_selectionCompleted_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_on_selectionCompleted_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_on_selectionCompleted_002 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_on_selectionCompleted_002', 0, async function (done) { + console.info('************* selectionfwk_on_selectionCompleted_002*************'); + try { + selectionManager.on('selectionCompleted'); + console.info(`selectionfwk_on_selectionCompleted_002 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_on_selectionCompleted_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_on_selectionCompleted_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_on_selectionCompleted_003 + * @tc.name Test should success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_on_selectionCompleted_003', 0, async function (done) { + console.info('************* selectionfwk_on_selectionCompleted_003*************'); + try { + selectionManager.on('selectionCompleted', (info) => { + console.info(`selectionfwk_on_selectionCompleted_003 callback enter`); + expect(true).assertTrue(); + done(); + }); + console.info(`selectionfwk_on_selectionCompleted_003 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_on_selectionCompleted_003 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + console.info('************* selectionfwk_on_selectionCompleted_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_on_selectionCompleted_004 + * @tc.name Test 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_on_selectionCompleted_004', 0, async function (done) { + console.info('************* selectionfwk_on_selectionCompleted_004*************'); + try { + selectionManager.on(null, (info) => { + console.info(`selectionfwk_on_selectionCompleted_004 callback enter`); + done(); + }); + console.info(`selectionfwk_on_selectionCompleted_004 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_on_selectionCompleted_004 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_on_selectionCompleted_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_on_selectionCompleted_005 + * @tc.name Test 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_on_selectionCompleted_005', 0, async function (done) { + console.info('************* selectionfwk_on_selectionCompleted_005*************'); + try { + selectionManager.on(undefined, (info) => { + console.info(`selectionfwk_on_selectionCompleted_005 callback enter`); + done(); + }); + console.info(`selectionfwk_on_selectionCompleted_005 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_on_selectionCompleted_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_on_selectionCompleted_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_on_selectionCompleted_006 + * @tc.name Test 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_on_selectionCompleted_006', 0, async function (done) { + console.info('************* selectionfwk_on_selectionCompleted_006*************'); + try { + selectionManager.on('selectionCompleted', null); + console.info(`selectionfwk_on_selectionCompleted_006 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_on_selectionCompleted_006 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_on_selectionCompleted_006 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_on_selectionCompleted_007 + * @tc.name Test 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_on_selectionCompleted_007', 0, async function (done) { + console.info('************* selectionfwk_on_selectionCompleted_007*************'); + try { + selectionManager.on('selectionCompleted', undefined); + console.info(`selectionfwk_on_selectionCompleted_007 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_on_selectionCompleted_007 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_on_selectionCompleted_007 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_off_selectionCompleted_001 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_off_selectionCompleted_001', 0, async function (done) { + console.info('************* selectionfwk_off_selectionCompleted_001*************'); + try { + selectionManager.off('selectionCompletedtest', (info) => { + console.info(`selectionfwk_off_selectionCompleted_001 is failed`); + done(); + }); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_off_selectionCompleted_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_off_selectionCompleted_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_off_selectionCompleted_002 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_off_selectionCompleted_002', 0, async function (done) { + console.info('************* selectionfwk_off_selectionCompleted_002*************'); + try { + selectionManager.off(); + console.info(`selectionfwk_off_selectionCompleted_002 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_off_selectionCompleted_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_off_selectionCompleted_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_off_selectionCompleted_003 + * @tc.name Test should success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_off_selectionCompleted_003', 0, async function (done) { + console.info('************* selectionfwk_off_selectionCompleted_003*************'); + try { + selectionManager.on('selectionCompleted', (info) => { + console.info(`selectionfwk_on_selectionCompleted_003 callback enter`); + done(); + }); + selectionManager.off('selectionCompleted'); + console.info(`selectionfwk_off_selectionCompleted_003 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_off_selectionCompleted_003 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + console.info('************* selectionfwk_off_selectionCompleted_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_off_selectionCompleted_004 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_off_selectionCompleted_004', 0, async function (done) { + console.info('************* selectionfwk_off_selectionCompleted_004*************'); + try { + selectionManager.on('selectionCompleted', (info) => { + console.info(`selectionfwk_off_selectionCompleted_004 callback enter`); + done(); + }); + selectionManager.off(null); + console.info(`selectionfwk_off_selectionCompleted_004 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_off_selectionCompleted_004 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_off_selectionCompleted_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_off_selectionCompleted_005 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_off_selectionCompleted_005', 0, async function (done) { + console.info('************* selectionfwk_off_selectionCompleted_005*************'); + try { + selectionManager.on('selectionCompleted', (info) => { + console.info(`selectionfwk_off_selectionCompleted_005 callback enter`); + done(); + }); + selectionManager.off(undefined); + console.info(`selectionfwk_off_selectionCompleted_005 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_off_selectionCompleted_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_off_selectionCompleted_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_off_selectionCompleted_006 + * @tc.name Test return success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_off_selectionCompleted_006', 0, async function (done) { + console.info('************* selectionfwk_off_selectionCompleted_006*************'); + try { + selectionManager.on('selectionCompleted', (info) => { + console.info(`selectionfwk_off_selectionCompleted_006 callback enter`); + done(); + }); + selectionManager.off('selectionCompleted', null); + console.info(`selectionfwk_off_selectionCompleted_006 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_off_selectionCompleted_006 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + console.info('************* selectionfwk_off_selectionCompleted_006 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_off_selectionCompleted_007 + * @tc.name Test return sucess, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_off_selectionCompleted_007', 0, async function (done) { + console.info('************* selectionfwk_off_selectionCompleted_007*************'); + try { + selectionManager.on('selectionCompleted', (info) => { + console.info(`selectionfwk_off_selectionCompleted_007 callback enter`); + done(); + }); + selectionManager.off('selectionCompleted', undefined); + console.info(`selectionfwk_off_selectionCompleted_007 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_off_selectionCompleted_007 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + console.info('************* selectionfwk_off_selectionCompleted_007 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_001 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_001', 0, async function (done) { + console.info('************* selectionfwk_createPanel_001 Test start*************'); + try { + await selectionManager.createPanel({stageMode : false}); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_002 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_002', 0, async function (done) { + console.info('************* selectionfwk_createPanel_002 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MENU_PANEL, + x: 0, + y: 0, + width: 100, + height: 100 + } + await selectionManager.createPanel(selectionManager.Context, panelInfo); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_003 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_003', 0, async function (done) { + console.info('************* selectionfwk_createPanel_003 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MENU_PANEL, + x: 0, + y: 0, + width: 100, + height: 100 + } + await selectionManager.createPanel({stageMode : true}, panelInfo); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_003 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_004 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_004', 0, async function (done) { + console.info('************* selectionfwk_createPanel_004 Test start*************'); + let panelTemp; + try { + let panelInfo = { + panelType: PanelType.MENU_PANEL, + x: 0, + y: 0, + width: 100, + height: 100 + } + await selectionManager.createPanel({stageMode : false}, panelInfo) + .then((panel) => { + panelTemp = panel; + expect(true).assertTrue(); + }) + await destroySelectionPanel(panelTemp); + } catch (error) { + console.info(`selectionfwk_createPanel_004 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + console.info('************* selectionfwk_createPanel_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_005 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_005', 0, async function (done) { + console.info('************* selectionfwk_createPanel_005 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MAIN_PANEL, + x: 0, + y: 0, + width: 100, + height: 100 + } + let panelTemp = await selectionManager.createPanel({stageMode : false}, panelInfo); + await destroySelectionPanel(panelTemp); + } catch (error) { + console.info(`selectionfwk_createPanel_005 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + console.info('************* selectionfwk_createPanel_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_006 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_006', 0, async function (done) { + console.info('************* selectionfwk_createPanel_006 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MAIN_PANEL, + x: -1, + y: 0, + width: 100, + height: 100 + } + await selectionManager.createPanel({stageMode : false}, panelInfo); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_006 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_006 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_007 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_007', 0, async function (done) { + console.info('************* selectionfwk_createPanel_007 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MAIN_PANEL, + x: 0, + y: -1, + width: 100, + height: 100 + } + await selectionManager.createPanel({stageMode : false}, panelInfo); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_007 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_007 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_008 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_008', 0, async function (done) { + console.info('************* selectionfwk_createPanel_008 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MAIN_PANEL, + x: 0, + y: 0, + width: -1, + height: 100 + } + await selectionManager.createPanel({stageMode : false}, panelInfo) + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_008 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_008 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_009 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_009', 0, async function (done) { + console.info('************* selectionfwk_createPanel_009 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MAIN_PANEL, + x: 0, + y: 0, + width: 100, + height: -1 + } + await selectionManager.createPanel({stageMode : false}, panelInfo) + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_009 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_009 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_010 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_010', 0, async function (done) { + console.info('************* selectionfwk_createPanel_010 Test start*************'); + try { + await selectionManager.createPanel({stageMode : false}, null); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_010 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_010 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_010 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_011', 0, async function (done) { + console.info('************* selectionfwk_createPanel_011 Test start*************'); + try { + await selectionManager.createPanel({stageMode : false}, undefined); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_011 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_011 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_012 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_012', 0, async function (done) { + console.info('************* selectionfwk_createPanel_012 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MAIN_PANEL, + x: 0, + y: 0, + width: 100, + height: 100 + } + await selectionManager.createPanel(null, panelInfo) + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_012 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_012 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_createPanel_013 + * @tc.name verify method of createPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_createPanel_013', 0, async function (done) { + console.info('************* selectionfwk_createPanel_013 Test start*************'); + try { + let panelInfo = { + panelType: PanelType.MAIN_PANEL, + x: 0, + y: 0, + width: 100, + height: 100 + } + await selectionManager.createPanel(undefined, panelInfo) + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_createPanel_013 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_createPanel_013 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_destroyPanel_001 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_destroyPanel_001', 0, async function (done) { + console.info('************* selectionfwk_destroyPanel_001 Test start*************'); + try { + await selectionManager.destroyPanel(); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_destroyPanel_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_destroyPanel_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_destroyPanel_002 + * @tc.name Test should throw BusinessError 401 when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_destroyPanel_002', 0, async function (done) { + console.info('************* selectionfwk_destroyPanel_002 Test start*************'); + try { + let panel = 1; + await selectionManager.destroyPanel(panel); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_destroyPanel_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_destroyPanel_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_destroyPanel_003 + * @tc.name verify method of destroyPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_destroyPanel_003', 0, async function (done) { + console.info('************* selectionfwk_destroyPanel_003 Test start*************'); + let panel = await createSelectionPanel(); + try { + await selectionManager.destroyPanel(panel); + console.info(`selectionfwk_destroyPanel_003 promise success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_destroyPanel_003 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + console.info('************* selectionfwk_destroyPanel_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_destroyPanel_004 + * @tc.name verify method of destroyPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_destroyPanel_004', 0, async function (done) { + console.info('************* selectionfwk_destroyPanel_004 Test start*************'); + try { + await selectionManager.destroyPanel(null); + console.info(`selectionfwk_destroyPanel_004 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_destroyPanel_004 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_destroyPanel_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_destroyPanel_005 + * @tc.name verify method of destroyPanel. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_destroyPanel_005', 0, async function (done) { + console.info('************* selectionfwk_destroyPanel_005 Test start*************'); + try { + await selectionManager.destroyPanel(undefined); + console.info(`selectionfwk_destroyPanel_005 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_destroyPanel_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + console.info('************* selectionfwk_destroyPanel_005 Test end*************'); + done(); + }); + + /* + * @tc.number selection_panel_setUiContent_001 + * @tc.name verify ERROR code 401 of setUiContent. + * @tc.desc Function test + * @tc.level 0 + */ + it('selection_panel_setUiContent_001', 0, async function (done) { + console.info('************* selection_panel_setUiContent_001 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.setUiContent(); + console.info(`selection_panel_setUiContent_001 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selection_panel_setUiContent_001 result: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selection_panel_setUiContent_001 Test end*************'); + done(); + }); + + /* + * @tc.number selection_panel_setUiContent_002 + * @tc.name verify ERROR code 401 of setUiContent. + * @tc.desc Function test + * @tc.level 0 + */ + it('selection_panel_setUiContent_002', 0, async function (done) { + console.info('************* selection_panel_setUiContent_002 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.setUiContent(20); + console.info(`selection_panel_setUiContent_002 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selection_panel_setUiContent_002 result: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selection_panel_setUiContent_002 Test end*************'); + done(); + }); + + /* + * @tc.number selection_panel_setUiContent_003 + * @tc.name verify ERROR code 33600002 of setUiContent. + * @tc.desc Function test + * @tc.level 0 + */ + it('selection_panel_setUiContent_003', 0, async function (done) { + console.info('************* selection_panel_setUiContent_003 Test start*************'); + let panel = await createSelectionPanel(); + await destroySelectionPanel(panel); + try { + await panel.setUiContent('pages/index/index'); + expect().assertFail(); + } catch (error) { + console.info(`selection_panel_setUiContent_003 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(33600002); + } + console.info('************* selection_panel_setUiContent_003 Test end*************'); + done(); + }); + + /* + * @tc.number selection_panel_setUiContent_004 + * @tc.name verify ERROR code 401 of setUiContent. + * @tc.desc Function test + * @tc.level 0 + */ + it('selection_panel_setUiContent_004', 0, async function (done) { + console.info('************* selection_panel_setUiContent_004 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.setUiContent(null); + expect().assertFail(); + } catch (error) { + console.info(`selection_panel_setUiContent_004 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selection_panel_setUiContent_004 Test end*************'); + done(); + }); + + /* + * @tc.number selection_panel_setUiContent_005 + * @tc.name verify ERROR code 401 of setUiContent. + * @tc.desc Function test + * @tc.level 0 + */ + it('selection_panel_setUiContent_005', 0, async function (done) { + console.info('************* selection_panel_setUiContent_005 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.setUiContent(undefined); + expect().assertFail(); + } catch (error) { + console.info(`selection_panel_setUiContent_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selection_panel_setUiContent_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_001 + * @tc.name verify ERROR code 401 of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_001', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_001 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(20); + console.info(`selectionfwk_panel_moveTo_001 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_002 + * @tc.name verify ERROR code 401 of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_002', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_002 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(20, '20'); + console.info(`selectionfwk_panel_moveTo_002 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_003 + * @tc.name verify ERROR code 33600002 the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_003', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_003 Test start*************'); + let panel = await createSelectionPanel(); + await destroySelectionPanel(panel); + try { + await panel.moveTo(20, 20); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_003 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(33600002); + } + console.info('************* selectionfwk_panel_moveTo_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_004 + * @tc.name verify method of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_004', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_004 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(50, 50); + console.info(`selectionfwk_panel_moveTo_004 promise success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_004 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_005 + * @tc.name verify method of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_005', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_005 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(-1, 50); + console.info(`selectionfwk_panel_moveTo_005 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_006 + * @tc.name verify method of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_006', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_006 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(50, -1); + console.info(`selectionfwk_panel_moveTo_006 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_006 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_006 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_007 + * @tc.name verify method of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_007', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_007 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(null, 50); + console.info(`selectionfwk_panel_moveTo_007 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_007 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_007 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_008 + * @tc.name verify method of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_008', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_008 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(undefined, 50); + console.info(`selectionfwk_panel_moveTo_008 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_008 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_008 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_009 + * @tc.name verify method of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_009', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_009 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(50, null); + console.info(`selectionfwk_panel_moveTo_009 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_009 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_009 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_moveTo_010 + * @tc.name verify method of the panel moveTo. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_moveTo_010', 0, async function (done) { + console.info('************* selectionfwk_panel_moveTo_010 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.moveTo(50, undefined); + console.info(`selectionfwk_panel_moveTo_010 promise success`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_moveTo_010 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_moveTo_010 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_show_001 + * @tc.name verify ERROR code 33600002 of the panel show. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_show_001', 0, async function (done) { + console.info('************* selectionfwk_panel_show_001 Test start*************'); + let panel = await createSelectionPanel(); + await destroySelectionPanel(panel); + try { + await panel.show(); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_show_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(33600002); + } + console.info('************* selectionfwk_panel_show_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_destroyed_001 + * @tc.name verify ERROR code 401 of the panel on destroyed. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_destroyed_001', 0, async function (done) { + console.info('************* selectionfwk_panel_on_destroyed_001*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyedTest', (info) => { + console.info(`selectionfwk_panel_on_destroyed_001 is failed`); + done(); + }); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_destroyed_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_destroyed_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_destroyed_002 + * @tc.name verify ERROR code 401 of the panel on destroyed. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_destroyed_002', 0, async function (done) { + console.info('************* selectionfwk_panel_on_destroyed_002*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed'); + console.info(`selectionfwk_panel_on_destroyed_002 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_destroyed_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_destroyed_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_destroyed_003 + * @tc.name Test should success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_destroyed_003', 0, async function (done) { + console.info('************* selectionfwk_panel_on_destroyed_003*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed', (info) => { + console.info(`selectionfwk_panel_on_destroyed_003 callback enter`); + done(); + }); + console.info(`selectionfwk_panel_on_destroyed_003 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_on_destroyed_003 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_destroyed_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_destroyed_004 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_destroyed_004', 0, async function (done) { + console.info('************* selectionfwk_panel_on_destroyed_004*************'); + let panel = await createSelectionPanel(); + try { + panel.on(null, (info) => { + console.info(`selectionfwk_panel_on_destroyed_004 callback enter`); + done(); + }); + console.info(`selectionfwk_panel_on_destroyed_004 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_destroyed_004 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_destroyed_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_destroyed_005 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_destroyed_005', 0, async function (done) { + console.info('************* selectionfwk_panel_on_destroyed_005*************'); + let panel = await createSelectionPanel(); + try { + panel.on(undefined, (info) => { + console.info(`selectionfwk_panel_on_destroyed_005 callback enter`); + done(); + }); + console.info(`selectionfwk_panel_on_destroyed_005 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_destroyed_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_destroyed_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_destroyed_006 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_destroyed_006', 0, async function (done) { + console.info('************* selectionfwk_panel_on_destroyed_006*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed', null); + console.info(`selectionfwk_panel_on_destroyed_006 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_destroyed_006 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_destroyed_006 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_destroyed_007 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_destroyed_007', 0, async function (done) { + console.info('************* selectionfwk_panel_on_destroyed_007*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed', undefined); + console.info(`selectionfwk_panel_on_destroyed_007 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_destroyed_007 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_destroyed_007 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_destroyed_001 + * @tc.name verify ERROR code 401 of the panel off destroyed. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_destroyed_001', 0, async function (done) { + console.info('************* selectionfwk_panel_off_destroyed_001*************'); + let panel = await createSelectionPanel(); + try { + panel.off('destroyedTest', (info) => { + console.info(`selectionfwk_panel_off_destroyed_001 is failed`); + done(); + }); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_off_destroyed_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_destroyed_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_destroyed_002 + * @tc.name verify ERROR code 401 of the panel on destroyed. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_destroyed_002', 0, async function (done) { + console.info('************* selectionfwk_panel_off_destroyed_002*************'); + let panel = await createSelectionPanel(); + try { + panel.off(() => { + console.info(`selectionfwk_panel_off_destroyed_002 is failed`); + expect().assertFail(); + }) + } catch (error) { + console.info(`selectionfwk_panel_off_destroyed_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_destroyed_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_destroyed_003 + * @tc.name Test should success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_destroyed_003', 0, async function (done) { + console.info('************* selectionfwk_panel_off_destroyed_003*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed', (info) => { + console.info(`selectionfwk_panel_off_destroyed_003 callback enter`); + done(); + }); + panel.off('destroyed'); + console.info(`selectionfwk_panel_off_destroyed_003 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_off_destroyed_003 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_destroyed_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_destroyed_004 + * @tc.name Test reurn 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_destroyed_004', 0, async function (done) { + console.info('************* selectionfwk_panel_off_destroyed_004*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed', (info) => { + console.info(`selectionfwk_panel_off_destroyed_004 callback enter`); + done(); + }); + panel.off(null); + console.info(`selectionfwk_panel_off_destroyed_004 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_off_destroyed_004 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_destroyed_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_destroyed_005 + * @tc.name Test reurn 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_destroyed_005', 0, async function (done) { + console.info('************* selectionfwk_panel_off_destroyed_005*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed', (info) => { + console.info(`selectionfwk_panel_off_destroyed_005 callback enter`); + done(); + }); + panel.off(undefined); + console.info(`selectionfwk_panel_off_destroyed_005 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_off_destroyed_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_destroyed_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_destroyed_006 + * @tc.name Test return success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_destroyed_006', 0, async function (done) { + console.info('************* selectionfwk_panel_off_destroyed_006*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed', (info) => { + console.info(`selectionfwk_panel_off_destroyed_006 callback enter`); + done(); + }); + panel.off('destroyed', null); + console.info(`selectionfwk_panel_off_destroyed_006 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_off_destroyed_006 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_destroyed_006 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_destroyed_007 + * @tc.name Test return success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_destroyed_007', 0, async function (done) { + console.info('************* selectionfwk_panel_off_destroyed_007*************'); + let panel = await createSelectionPanel(); + try { + panel.on('destroyed', (info) => { + console.info(`selectionfwk_panel_off_destroyed_007 callback enter`); + done(); + }); + panel.off('destroyed', undefined); + console.info(`selectionfwk_panel_off_destroyed_007 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_off_destroyed_007 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_destroyed_007 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_hidden_001 + * @tc.name verify ERROR code 401 of the panel on hidden. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_hidden_001', 0, async function (done) { + console.info('************* selectionfwk_panel_on_hidden_001*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hiddenTest', (info) => { + console.info(`selectionfwk_panel_on_hidden_001 is failed`); + done(); + }); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_hidden_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_hidden_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_hidden_002 + * @tc.name verify ERROR code 401 of the panel on hidden. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_hidden_002', 0, async function (done) { + console.info('************* selectionfwk_panel_on_hidden_002*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden'); + console.info(`selectionfwk_panel_on_hidden_002 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_hidden_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_hidden_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_hidden_003 + * @tc.name Test should success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_hidden_003', 0, async function (done) { + console.info('************* selectionfwk_panel_on_hidden_003*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden', (info) => { + console.info(`selectionfwk_panel_on_hidden_003 callback enter`); + done(); + }); + console.info(`selectionfwk_panel_on_hidden_003 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_on_hidden_003 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_hidden_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_hidden_004 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_hidden_004', 0, async function (done) { + console.info('************* selectionfwk_panel_on_hidden_004*************'); + let panel = await createSelectionPanel(); + try { + panel.on(null, (info) => { + console.info(`selectionfwk_panel_on_hidden_004 callback enter`); + done(); + }); + console.info(`selectionfwk_panel_on_hidden_004 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_hidden_004 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_hidden_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_hidden_005 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_hidden_005', 0, async function (done) { + console.info('************* selectionfwk_panel_on_hidden_005*************'); + let panel = await createSelectionPanel(); + try { + panel.on(undefined, (info) => { + console.info(`selectionfwk_panel_on_hidden_005 callback enter`); + done(); + }); + console.info(`selectionfwk_panel_on_hidden_005 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_hidden_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_hidden_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_hidden_006 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_hidden_006', 0, async function (done) { + console.info('************* selectionfwk_panel_on_hidden_006*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden', null); + console.info(`selectionfwk_panel_on_hidden_006 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_hidden_006 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_hidden_006 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_on_hidden_007 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_on_hidden_007', 0, async function (done) { + console.info('************* selectionfwk_panel_on_hidden_007*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden', undefined); + console.info(`selectionfwk_panel_on_hidden_007 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_on_hidden_007 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_on_hidden_007 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_hidden_001 + * @tc.name verify ERROR code 401 of the panel off hidden. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_hidden_001', 0, async function (done) { + console.info('************* selectionfwk_panel_off_hidden_001*************'); + let panel = await createSelectionPanel(); + try { + panel.off('hiddenTest', (info) => { + console.info(`selectionfwk_panel_off_hidden_001 is failed`); + done(); + }); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_off_hidden_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_hidden_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_hidden_002 + * @tc.name verify ERROR code 401 of the panel on hidden. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_hidden_002', 0, async function (done) { + console.info('************* selectionfwk_panel_off_hidden_002*************'); + let panel = await createSelectionPanel(); + try { + panel.off(() => { + console.info(`selectionfwk_panel_off_hidden_002 is failed`); + expect().assertFail(); + }) + } catch (error) { + console.info(`selectionfwk_panel_off_hidden_002 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_hidden_002 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_hidden_003 + * @tc.name Test should success, when parameters are valid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_hidden_003', 0, async function (done) { + console.info('************* selectionfwk_panel_off_hidden_003*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden', (info) => { + console.info(`selectionfwk_panel_off_hidden_003 callback enter`); + done(); + }); + panel.off('hidden'); + console.info(`selectionfwk_panel_off_hidden_003 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_off_hidden_003 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_hidden_003 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_hidden_004 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_hidden_004', 0, async function (done) { + console.info('************* selectionfwk_panel_off_hidden_004*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden', (info) => { + console.info(`selectionfwk_panel_off_hidden_004 callback enter`); + done(); + }); + panel.off(null); + console.info(`selectionfwk_panel_off_hidden_004 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_off_hidden_004 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_hidden_004 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_hidden_005 + * @tc.name Test return 401, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_hidden_005', 0, async function (done) { + console.info('************* selectionfwk_panel_off_hidden_005*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden', (info) => { + console.info(`selectionfwk_panel_off_hidden_005 callback enter`); + done(); + }); + panel.off(undefined); + console.info(`selectionfwk_panel_off_hidden_005 is failed`); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_off_hidden_005 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(401); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_hidden_005 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_hidden_006 + * @tc.name Test return success, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_hidden_006', 0, async function (done) { + console.info('************* selectionfwk_panel_off_hidden_006*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden', (info) => { + console.info(`selectionfwk_panel_off_hidden_006 callback enter`); + done(); + }); + panel.off('hidden', null); + console.info(`selectionfwk_panel_off_hidden_006 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_off_hidden_006 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_hidden_006 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_off_hidden_007 + * @tc.name Test return success, when parameters are invalid. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_off_hidden_007', 0, async function (done) { + console.info('************* selectionfwk_panel_off_hidden_007*************'); + let panel = await createSelectionPanel(); + try { + panel.on('hidden', (info) => { + console.info(`selectionfwk_panel_off_hidden_007 callback enter`); + done(); + }); + panel.off('hidden', undefined); + console.info(`selectionfwk_panel_off_hidden_007 is success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_off_hidden_007 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_off_hidden_007 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_hide_001 + * @tc.name verify ERROR code 33600002 of the panel hide. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_hide_001', 0, async function (done) { + console.info('************* selectionfwk_panel_hide_001 Test start*************'); + let panel = await createSelectionPanel(); + await destroySelectionPanel(panel); + try { + await panel.hide(); + expect().assertFail(); + } catch (error) { + console.info(`selectionfwk_panel_hide_001 throw error: ${JSON.stringify(error)}`); + expect(error.code).assertEqual(33600002); + } + console.info('************* selectionfwk_panel_hide_001 Test end*************'); + done(); + }); + + /* + * @tc.number selectionfwk_panel_hide_002 + * @tc.name verify method of the panel hide. + * @tc.desc Function test + * @tc.level 0 + */ + it('selectionfwk_panel_hide_002', 0, async function (done) { + console.info('************* selectionfwk_panel_hide_002 Test start*************'); + let panel = await createSelectionPanel(); + try { + await panel.hide(); + console.info(`selectionfwk_panel_hide_002 promise success`); + expect(true).assertTrue(); + } catch (error) { + console.info(`selectionfwk_panel_hide_002 throw error: ${JSON.stringify(error)}`); + expect().assertFail(); + } + await destroySelectionPanel(panel); + console.info('************* selectionfwk_panel_hide_002 Test end*************'); + done(); + }); +}); diff --git a/test/unittest/selection_manager_js_test/config.json b/test/unittest/selection_manager_js_test/config.json new file mode 100644 index 0000000000000000000000000000000000000000..46ca512375d883ff5a9f044c52ba034db65d37f7 --- /dev/null +++ b/test/unittest/selection_manager_js_test/config.json @@ -0,0 +1,63 @@ +{ + "app": { + "bundleName": "com.example.selectionmanagertest", + "vendor": "example", + "version": { + "code": 1, + "name": "1.0" + }, + "apiVersion": { + "compatible": 20, + "target": 20 + } + }, + "deviceConfig": {}, + "module": { + "reqPermissions": [], + "package": "com.example.selectionmanagertest", + "name": ".MyApplication", + "deviceType": [ + "default", + "tablet", + "2in1" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry" + }, + "abilities": [ + { + "visible": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "name": "com.example.selectionmanagertest.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "PermissionJsTest", + "type": "page", + "launchType": "standard" + } + ], + "js": [ + { + "pages": [ + "pages/index/index" + ], + "name": "default", + "window": { + "designWidth": 720, + "autoDesignWidth": false + } + } + ] + } +} diff --git a/test/unittest/selection_manager_js_test/openharmony_sx.p7b b/test/unittest/selection_manager_js_test/openharmony_sx.p7b new file mode 100644 index 0000000000000000000000000000000000000000..3c2b57903f2afe51f8eeb1e1a84fdf8d7bcfe410 Binary files /dev/null and b/test/unittest/selection_manager_js_test/openharmony_sx.p7b differ diff --git a/utils/include/selection_errors.h b/utils/include/selection_errors.h new file mode 100644 index 0000000000000000000000000000000000000000..6b542d20c06593823d5a5d29909329afd99ddb1c --- /dev/null +++ b/utils/include/selection_errors.h @@ -0,0 +1,40 @@ +/* + * 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 SELECTION_ERRORS_H +#define SELECTION_ERRORS_H + +#include +#include +#include + +namespace OHOS { +namespace SelectionFwk { + +enum SelectionConfigErrCode { + SELECTION_CONFIG_OK = 0, + SELECTION_CONFIG_FAILURE = -1, + SELECTION_CONFIG_RDB_EXECUTE_FAILTURE = -2, + SELECTION_CONFIG_RDB_NO_INIT = -3, + SELECTION_CONFIG_RDB_EMPTY = -4, + SELECTION_CONFIG_PERMISSION_DENIED = -5, + SELECTION_CONFIG_NOP = -6, + SELECTION_CONFIG_OVERFLOW = -7, + SELECTION_CONFIG_NOT_FOUND = -8, +}; + +} // namespace USB +} // namespace OHOS +#endif // SELECTION_ERRORS_H diff --git a/utils/include/selection_log.h b/utils/include/selection_log.h new file mode 100644 index 0000000000000000000000000000000000000000..fa524636ea2f3b772e38a0b9c91e6d0bccbffc0d --- /dev/null +++ b/utils/include/selection_log.h @@ -0,0 +1,69 @@ +/* + * 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 SELECTION_LOG_H +#define SELECTION_LOG_H +#include "hilog/log.h" +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#undef LOG_TAG +#define LOG_TAG "SELECTION_SERVICE" +#undef LOG_DOMAIN +#define LOG_DOMAIN 0xD002901 + +#ifndef SELECTION_DEBUG_ENABLE +#define SELECTION_DEBUG_ENABLE 0 +#endif + +#define __BASE_FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) + +#define SELECTION_HILOGF(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_FATAL, LOG_DOMAIN, LOG_TAG, "[%{public}s(%{public}s:%{public}d)]" fmt, \ + __BASE_FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__)) +#define SELECTION_HILOGE(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_ERROR, LOG_DOMAIN, LOG_TAG, "[%{public}s(%{public}s:%{public}d)]" fmt, \ + __BASE_FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__)) +#define SELECTION_HILOGW(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_WARN, LOG_DOMAIN, LOG_TAG, "[%{public}s(%{public}s:%{public}d)]" fmt, \ + __BASE_FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__)) +#define SELECTION_HILOGI(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_INFO, LOG_DOMAIN, LOG_TAG, "[%{public}s(%{public}s:%{public}d)]" fmt, \ + __BASE_FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__)) +#define SELECTION_HILOGD(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_DEBUG, LOG_DOMAIN, LOG_TAG, "[%{public}s(%{public}s:%{public}d)]" fmt, \ + __BASE_FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__)) + +namespace ErrorCode { +// Error Code definition in the input method management system +enum { + // no error + NO_ERROR = 0, // no error + ERROR_PARAMETER_CHECK_FAILED, + + ERROR_SELECTION_SERVICE, + ERROR_PANEL_DESTROYED, + ERROR_INVALID_OPERATION +}; +}; // namespace ErrorCode + +#ifdef __cplusplus +} +#endif + +#endif // SELECTION_LOG_H \ No newline at end of file