1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "core/common/recorder/event_controller.h"
16
17 #include "base/thread/background_task_executor.h"
18 #include "core/common/recorder/node_data_cache.h"
19 #include "core/pipeline_ng/pipeline_context.h"
20
21 namespace OHOS::Ace::Recorder {
22 constexpr int32_t PAGE_URL_SUFFIX_LENGTH = 3;
23 constexpr uint32_t EXPOSURE_REGISTER_DELAY = 500;
24
25 struct ExposureWrapper {
26 WeakPtr<NG::FrameNode> node;
27 RefPtr<ExposureProcessor> processor;
28
ExposureWrapperOHOS::Ace::Recorder::ExposureWrapper29 ExposureWrapper(const WeakPtr<NG::FrameNode>& node, RefPtr<ExposureProcessor>&& processor)
30 : node(node), processor(processor)
31 {}
32 };
33
Get()34 EventController& EventController::Get()
35 {
36 static EventController eventController;
37 return eventController;
38 }
39
Register(const std::string & config,const std::shared_ptr<UIEventObserver> & observer)40 void EventController::Register(const std::string& config, const std::shared_ptr<UIEventObserver>& observer)
41 {
42 TAG_LOGI(AceLogTag::ACE_UIEVENT, "Register config");
43 UIEventClient client;
44 client.config.Init(config);
45 if (!client.config.IsEnable()) {
46 return;
47 }
48 client.observer = observer;
49 std::unique_lock<std::shared_mutex> lock(cacheLock_);
50 clientList_.emplace_back(std::move(client));
51 lock.unlock();
52 bool isOriginEnable = EventRecorder::Get().IsExposureRecordEnable();
53 NotifyConfigChange();
54 bool isCurrentEnable = EventRecorder::Get().IsExposureRecordEnable();
55 if (!isOriginEnable && isCurrentEnable) {
56 ApplyNewestConfig();
57 }
58 TAG_LOGI(AceLogTag::ACE_UIEVENT, "Register config end");
59 }
60
NotifyConfigChange()61 void EventController::NotifyConfigChange()
62 {
63 std::shared_lock<std::shared_mutex> lock(cacheLock_);
64 auto mergedConfig = std::make_shared<MergedConfig>();
65 EventSwitch eventSwitch;
66 for (auto&& client : clientList_) {
67 if (!client.config.IsEnable()) {
68 continue;
69 }
70 eventSwitch.pageEnable = eventSwitch.pageEnable || client.config.IsCategoryEnable(EventCategory::CATEGORY_PAGE);
71 eventSwitch.exposureEnable =
72 eventSwitch.exposureEnable || client.config.IsCategoryEnable(EventCategory::CATEGORY_EXPOSURE);
73 eventSwitch.componentEnable =
74 eventSwitch.componentEnable || client.config.IsCategoryEnable(EventCategory::CATEGORY_COMPONENT);
75 eventSwitch.pageParamEnable =
76 eventSwitch.pageParamEnable || client.config.IsCategoryEnable(EventCategory::CATEGORY_PAGE_PARAM);
77 for (auto iter = client.config.GetConfig()->begin(); iter != client.config.GetConfig()->end(); iter++) {
78 auto nodeIt = mergedConfig->shareNodes.find(iter->first);
79 if (nodeIt != mergedConfig->shareNodes.end()) {
80 std::for_each(iter->second.shareNodes.begin(), iter->second.shareNodes.end(),
81 [&nodeIt](const std::list<std::string>::value_type& id) { nodeIt->second.emplace(id); });
82 } else {
83 std::unordered_set<std::string> nodeSet;
84 std::for_each(iter->second.shareNodes.begin(), iter->second.shareNodes.end(),
85 [&nodeSet](const std::list<std::string>::value_type& id) { nodeSet.emplace(id); });
86 mergedConfig->shareNodes.emplace(iter->first, std::move(nodeSet));
87 }
88
89 auto exposureIt = mergedConfig->exposureNodes.find(iter->first);
90 if (exposureIt != mergedConfig->exposureNodes.end()) {
91 std::for_each(iter->second.exposureCfgs.begin(), iter->second.exposureCfgs.end(),
92 [&exposureIt](
93 const std::list<ExposureCfg>::value_type& cfg) { exposureIt->second.emplace(cfg); });
94 } else {
95 std::unordered_set<ExposureCfg, ExposureCfgHash> exposureSet;
96 std::for_each(iter->second.exposureCfgs.begin(), iter->second.exposureCfgs.end(),
97 [&exposureSet](const std::list<ExposureCfg>::value_type& cfg) { exposureSet.emplace(cfg); });
98 mergedConfig->exposureNodes.emplace(iter->first, std::move(exposureSet));
99 }
100 }
101 }
102 NodeDataCache::Get().UpdateConfig(std::move(mergedConfig));
103 EventRecorder::Get().UpdateEventSwitch(eventSwitch);
104 }
105
GetPageUrlByContainerId(const int32_t containerId)106 std::string GetPageUrlByContainerId(const int32_t containerId)
107 {
108 auto container = Container::GetContainer(containerId);
109 CHECK_NULL_RETURN(container, "");
110 if (!container->IsUseNewPipeline()) {
111 return "";
112 }
113 auto frontEnd = container->GetFrontend();
114 CHECK_NULL_RETURN(frontEnd, "");
115 auto pageUrl = frontEnd->GetCurrentPageUrl();
116 if (StringUtils::EndWith(pageUrl, ".js")) {
117 pageUrl = pageUrl.substr(0, pageUrl.length() - PAGE_URL_SUFFIX_LENGTH);
118 }
119 return pageUrl;
120 }
121
GetMatchedNodes(const std::string & pageUrl,const RefPtr<NG::UINode> & root,const std::unordered_set<ExposureCfg,ExposureCfgHash> & exposureSet,std::list<ExposureWrapper> & outputList)122 void GetMatchedNodes(const std::string& pageUrl, const RefPtr<NG::UINode>& root,
123 const std::unordered_set<ExposureCfg, ExposureCfgHash>& exposureSet, std::list<ExposureWrapper>& outputList)
124 {
125 std::queue<RefPtr<NG::UINode>> elements;
126 ExposureCfg targetCfg = { "", 0.0, 0 };
127 elements.push(root);
128 while (!elements.empty()) {
129 auto current = elements.front();
130 elements.pop();
131 targetCfg.id = current->GetInspectorIdValue("");
132 if (!targetCfg.id.empty() && AceType::InstanceOf<NG::FrameNode>(current)) {
133 auto frameNode = AceType::DynamicCast<NG::FrameNode>(current);
134 auto cfgIter = exposureSet.find(targetCfg);
135 if (cfgIter != exposureSet.end()) {
136 outputList.emplace_back(ExposureWrapper(Referenced::WeakClaim(Referenced::RawPtr(frameNode)),
137 Referenced::MakeRefPtr<ExposureProcessor>(
138 pageUrl, targetCfg.id, cfgIter->ratio, cfgIter->duration)));
139 }
140 }
141 for (const auto& child : current->GetChildren()) {
142 elements.push(child);
143 }
144 }
145 }
146
ApplyNewestConfig() const147 void EventController::ApplyNewestConfig() const
148 {
149 std::shared_lock<std::shared_mutex> lock(cacheLock_);
150 if (clientList_.empty()) {
151 return;
152 }
153 auto containerId = EventRecorder::Get().GetContainerId();
154 auto config = clientList_.back().config.GetConfig();
155
156 auto context = NG::PipelineContext::GetContextByContainerId(containerId);
157 CHECK_NULL_VOID(context);
158 auto taskExecutor = context->GetTaskExecutor();
159 CHECK_NULL_VOID(taskExecutor);
160 taskExecutor->PostDelayedTask([config]() { EventController::Get().ApplyExposureCfgInner(config); },
161 TaskExecutor::TaskType::UI, EXPOSURE_REGISTER_DELAY, "EventController");
162 }
163
ApplyExposureCfgInner(const std::shared_ptr<Config> & config) const164 void EventController::ApplyExposureCfgInner(const std::shared_ptr<Config>& config) const
165 {
166 auto containerId = EventRecorder::Get().GetContainerId();
167 auto pageUrl = GetPageUrlByContainerId(containerId);
168 if (pageUrl.empty()) {
169 return;
170 }
171 auto pageIter = config->find(pageUrl);
172 if (pageIter == config->end()) {
173 return;
174 }
175 if (pageIter->second.exposureCfgs.empty()) {
176 return;
177 }
178 auto context = NG::PipelineContext::GetContextByContainerId(containerId);
179 CHECK_NULL_VOID(context);
180 auto rootNode = context->GetRootElement();
181 CHECK_NULL_VOID(rootNode);
182 std::unordered_set<ExposureCfg, ExposureCfgHash> exposureSet;
183 std::for_each(pageIter->second.exposureCfgs.begin(), pageIter->second.exposureCfgs.end(),
184 [&exposureSet](const std::list<ExposureCfg>::value_type& cfg) { exposureSet.emplace(cfg); });
185 std::list<ExposureWrapper> targets;
186 GetMatchedNodes(pageUrl, rootNode, exposureSet, targets);
187 for (auto& item : targets) {
188 item.processor->SetContainerId(containerId);
189 auto node = item.node.Upgrade();
190 CHECK_NULL_VOID(node);
191 node->SetExposureProcessor(item.processor);
192 }
193 }
194
Unregister(const std::shared_ptr<UIEventObserver> & observer)195 void EventController::Unregister(const std::shared_ptr<UIEventObserver>& observer)
196 {
197 std::unique_lock<std::shared_mutex> lock(cacheLock_);
198 auto iter = std::remove_if(clientList_.begin(), clientList_.end(),
199 [&observer](UIEventClient client) { return client.observer == observer; });
200 bool change = iter != clientList_.end();
201 clientList_.erase(iter, clientList_.end());
202 lock.unlock();
203 if (change) {
204 NotifyConfigChange();
205 }
206 }
207
NotifyEvent(EventCategory category,int32_t eventType,const std::shared_ptr<std::unordered_map<std::string,std::string>> & eventParams)208 void EventController::NotifyEvent(EventCategory category, int32_t eventType,
209 const std::shared_ptr<std::unordered_map<std::string, std::string>>& eventParams)
210 {
211 {
212 std::shared_lock<std::shared_mutex> lock(cacheLock_);
213 if (clientList_.empty()) {
214 return;
215 }
216 }
217 BackgroundTaskExecutor::GetInstance().PostTask([category, eventType, eventParams]() {
218 EventController::Get().NotifyEventSync(category, eventType, eventParams);
219 });
220 }
221
NotifyEventSync(EventCategory category,int32_t eventType,const std::shared_ptr<std::unordered_map<std::string,std::string>> & eventParams)222 void EventController::NotifyEventSync(EventCategory category, int32_t eventType,
223 const std::shared_ptr<std::unordered_map<std::string, std::string>>& eventParams)
224 {
225 std::shared_lock<std::shared_mutex> lock(cacheLock_);
226 for (auto&& client : clientList_) {
227 if (client.config.IsEnable() && client.config.IsCategoryEnable(category)) {
228 client.observer->NotifyUIEvent(eventType, *eventParams);
229 }
230 }
231 }
232 } // namespace OHOS::Ace::Recorder
233