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 #include <cstdint>
17 #include <vector>
18
19 #include "base/thread/background_task_executor.h"
20 #include "core/common/recorder/event_definition.h"
21 #include "core/common/recorder/event_recorder.h"
22 #include "core/common/recorder/node_data_cache.h"
23 #include "core/components_v2/inspector/inspector_constants.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25
26 namespace OHOS::Ace::Recorder {
27 constexpr uint32_t EXPOSURE_REGISTER_DELAY = 500;
28 constexpr int32_t MAX_CACHE_SIZE = 5;
29
30 struct ExposureWrapper {
31 WeakPtr<NG::FrameNode> node;
32 RefPtr<ExposureProcessor> processor;
33
ExposureWrapperOHOS::Ace::Recorder::ExposureWrapper34 ExposureWrapper(const WeakPtr<NG::FrameNode>& node, RefPtr<ExposureProcessor>&& processor)
35 : node(node), processor(processor)
36 {}
37 };
38
Get()39 EventController& EventController::Get()
40 {
41 static EventController eventController;
42 return eventController;
43 }
44
Register(const std::string & config,const std::shared_ptr<UIEventObserver> & observer)45 void EventController::Register(const std::string& config, const std::shared_ptr<UIEventObserver>& observer)
46 {
47 TAG_LOGI(AceLogTag::ACE_UIEVENT, "Register config");
48 CHECK_NULL_VOID(observer);
49 UIEventClient client;
50 client.config.Init(config);
51 if (!client.config.IsEnable()) {
52 return;
53 }
54 client.observer = observer;
55 std::unique_lock<std::shared_mutex> lock(cacheLock_);
56 clientList_.emplace_back(client);
57 lock.unlock();
58 bool isOriginExposureEnable = EventRecorder::Get().IsExposureRecordEnable();
59 NotifyConfigChange();
60 bool isExposureChanged = EventRecorder::Get().IsExposureRecordEnable() && !isOriginExposureEnable;
61 if (isExposureChanged) {
62 ApplyNewestConfig(isExposureChanged);
63 }
64 NotifyCacheEventsIfNeed(client);
65 TAG_LOGI(AceLogTag::ACE_UIEVENT, "Register config end");
66 }
67
NotifyConfigChange()68 void EventController::NotifyConfigChange()
69 {
70 std::shared_lock<std::shared_mutex> lock(cacheLock_);
71 auto mergedConfig = std::make_shared<MergedConfig>();
72 int32_t size = static_cast<int32_t>(EventCategory::CATEGORY_END);
73 std::vector<bool> eventSwitch;
74 eventSwitch.resize(size, false);
75 eventSwitch[static_cast<int32_t>(EventCategory::CATEGORY_PAGE)] = true;
76 std::unordered_map<std::string, std::string> webIdentifierMap;
77 for (auto&& client : clientList_) {
78 if (!client.config.IsEnable()) {
79 continue;
80 }
81 for (int32_t i = 0; i < size; i++) {
82 eventSwitch[i] = eventSwitch[i] || client.config.IsCategoryEnable(i);
83 }
84 for (auto iter = client.config.GetConfig()->begin(); iter != client.config.GetConfig()->end(); iter++) {
85 auto nodeIt = mergedConfig->shareNodes.find(iter->first);
86 if (nodeIt != mergedConfig->shareNodes.end()) {
87 std::for_each(iter->second.shareNodes.begin(), iter->second.shareNodes.end(),
88 [&nodeIt](const std::list<std::string>::value_type& id) { nodeIt->second.emplace(id); });
89 } else {
90 std::unordered_set<std::string> nodeSet;
91 std::for_each(iter->second.shareNodes.begin(), iter->second.shareNodes.end(),
92 [&nodeSet](const std::list<std::string>::value_type& id) { nodeSet.emplace(id); });
93 mergedConfig->shareNodes.emplace(iter->first, std::move(nodeSet));
94 }
95
96 auto exposureIt = mergedConfig->exposureNodes.find(iter->first);
97 if (exposureIt != mergedConfig->exposureNodes.end()) {
98 std::for_each(iter->second.exposureCfgs.begin(), iter->second.exposureCfgs.end(),
99 [&exposureIt](
100 const std::list<ExposureCfg>::value_type& cfg) { exposureIt->second.emplace(cfg); });
101 } else {
102 std::unordered_set<ExposureCfg, ExposureCfgHash> exposureSet;
103 std::for_each(iter->second.exposureCfgs.begin(), iter->second.exposureCfgs.end(),
104 [&exposureSet](const std::list<ExposureCfg>::value_type& cfg) { exposureSet.emplace(cfg); });
105 mergedConfig->exposureNodes.emplace(iter->first, std::move(exposureSet));
106 }
107 }
108 if (!client.config.GetWebCategory().empty()) {
109 webIdentifierMap[client.config.GetWebCategory()] = client.config.GetWebIdentifier();
110 }
111 }
112 NodeDataCache::Get().UpdateConfig(std::move(mergedConfig));
113 EventRecorder::Get().UpdateEventSwitch(eventSwitch);
114 EventRecorder::Get().UpdateWebIdentifier(webIdentifierMap);
115 }
116
IsAllowNotify(const EventConfig & config,EventCategory category,int32_t eventType,const std::shared_ptr<std::unordered_map<std::string,std::string>> & eventParams)117 bool IsAllowNotify(const EventConfig& config, EventCategory category, int32_t eventType,
118 const std::shared_ptr<std::unordered_map<std::string, std::string>>& eventParams)
119 {
120 CHECK_NULL_RETURN(eventParams, false);
121 auto enable = config.IsEnable() && config.IsCategoryEnable(static_cast<int32_t>(category));
122 if (!enable) {
123 return false;
124 }
125 if (eventType == EventType::WEB_ACTION) {
126 return eventParams->count(KEY_WEB_CATEGORY) > 0 && eventParams->at(KEY_WEB_CATEGORY) == config.GetWebCategory();
127 }
128 return true;
129 }
130
NotifyCacheEventsIfNeed(const UIEventClient & client) const131 void EventController::NotifyCacheEventsIfNeed(const UIEventClient& client) const
132 {
133 std::shared_lock<std::shared_mutex> lock(cacheEventLock_);
134 if (cacheEvents_.empty()) {
135 return;
136 }
137 BackgroundTaskExecutor::GetInstance().PostTask([events = cacheEvents_, client]() {
138 for (const auto& event : events) {
139 if (IsAllowNotify(client.config, event.category, event.eventType, event.eventParams)) {
140 client.observer->NotifyUIEvent(event.eventType, event.eventParams);
141 }
142 }
143 });
144 }
145
GetMatchedNodes(const std::string & pageUrl,const RefPtr<NG::UINode> & root,const std::unordered_set<ExposureCfg,ExposureCfgHash> & exposureSet,std::list<ExposureWrapper> & outputList)146 void GetMatchedNodes(const std::string& pageUrl, const RefPtr<NG::UINode>& root,
147 const std::unordered_set<ExposureCfg, ExposureCfgHash>& exposureSet, std::list<ExposureWrapper>& outputList)
148 {
149 std::queue<RefPtr<NG::UINode>> elements;
150 ExposureCfg targetCfg = { "", 0.0, 0 };
151 elements.push(root);
152 while (!elements.empty()) {
153 auto current = elements.front();
154 elements.pop();
155 targetCfg.id = current->GetInspectorIdValue("");
156 if (!targetCfg.id.empty() && AceType::InstanceOf<NG::FrameNode>(current)) {
157 auto frameNode = AceType::DynamicCast<NG::FrameNode>(current);
158 auto cfgIter = exposureSet.find(targetCfg);
159 if (cfgIter != exposureSet.end()) {
160 outputList.emplace_back(ExposureWrapper(Referenced::WeakClaim(Referenced::RawPtr(frameNode)),
161 Referenced::MakeRefPtr<ExposureProcessor>(
162 pageUrl, targetCfg.id, cfgIter->ratio, cfgIter->duration)));
163 }
164 }
165 for (const auto& child : current->GetChildren()) {
166 elements.push(child);
167 }
168 }
169 }
170
ApplyNewestConfig(bool isExposureChanged) const171 void EventController::ApplyNewestConfig(bool isExposureChanged) const
172 {
173 TAG_LOGI(AceLogTag::ACE_UIEVENT, "ApplyNewestConfig isExposureChanged:%{public}d", isExposureChanged);
174 std::shared_lock<std::shared_mutex> lock(cacheLock_);
175 if (clientList_.empty()) {
176 return;
177 }
178 auto containerId = EventRecorder::Get().GetContainerId();
179 auto config = clientList_.back().config.GetConfig();
180
181 auto context = NG::PipelineContext::GetContextByContainerId(containerId);
182 CHECK_NULL_VOID(context);
183 auto taskExecutor = context->GetTaskExecutor();
184 CHECK_NULL_VOID(taskExecutor);
185 taskExecutor->PostDelayedTask(
186 [config, isExposureChanged]() {
187 EventController::Get().ApplyExposureCfgInner(config, isExposureChanged);
188 },
189 TaskExecutor::TaskType::UI, EXPOSURE_REGISTER_DELAY, "EventController");
190 }
191
ApplyExposureCfgInner(const std::shared_ptr<Config> & config,bool isExposureChanged) const192 void EventController::ApplyExposureCfgInner(const std::shared_ptr<Config>& config, bool isExposureChanged) const
193 {
194 auto containerId = EventRecorder::Get().GetContainerId();
195 auto pageUrl = GetPageUrlByContainerId(containerId);
196 if (pageUrl.empty()) {
197 return;
198 }
199 auto context = NG::PipelineContext::GetContextByContainerId(containerId);
200 CHECK_NULL_VOID(context);
201 auto rootNode = context->GetRootElement();
202 CHECK_NULL_VOID(rootNode);
203 std::unordered_set<ExposureCfg, ExposureCfgHash> exposureSet;
204 std::list<ExposureWrapper> targets;
205
206 if (isExposureChanged) {
207 auto pageIter = config->find(pageUrl);
208 if (pageIter != config->end() && pageIter->second.exposureCfgs.size() > 0) {
209 std::for_each(pageIter->second.exposureCfgs.begin(), pageIter->second.exposureCfgs.end(),
210 [&exposureSet](const std::list<ExposureCfg>::value_type& cfg) { exposureSet.emplace(cfg); });
211 }
212 }
213 GetMatchedNodes(pageUrl, rootNode, exposureSet, targets);
214 for (auto& item : targets) {
215 item.processor->SetContainerId(containerId);
216 auto node = item.node.Upgrade();
217 CHECK_NULL_VOID(node);
218 node->SetExposureProcessor(item.processor);
219 }
220 }
221
Unregister(const std::shared_ptr<UIEventObserver> & observer)222 void EventController::Unregister(const std::shared_ptr<UIEventObserver>& observer)
223 {
224 std::unique_lock<std::shared_mutex> lock(cacheLock_);
225 auto iter = std::remove_if(clientList_.begin(), clientList_.end(),
226 [&observer](const UIEventClient& client) { return client.observer == observer; });
227 bool change = iter != clientList_.end();
228 clientList_.erase(iter, clientList_.end());
229 lock.unlock();
230 if (change) {
231 NotifyConfigChange();
232 }
233 }
234
CacheEventIfNeed(EventCategory category,int32_t eventType,const std::shared_ptr<std::unordered_map<std::string,std::string>> & eventParams)235 void EventController::CacheEventIfNeed(EventCategory category, int32_t eventType,
236 const std::shared_ptr<std::unordered_map<std::string, std::string>>& eventParams)
237 {
238 std::unique_lock<std::shared_mutex> lock(cacheEventLock_);
239 if (cacheEvents_.empty()) {
240 if (hasCached_) {
241 return;
242 } else {
243 cacheEvents_.emplace_back(CacheEvent { category, eventType, eventParams });
244 }
245 } else if (cacheEvents_.size() < MAX_CACHE_SIZE) {
246 cacheEvents_.emplace_back(CacheEvent { category, eventType, eventParams });
247 } else {
248 hasCached_ = true;
249 cacheEvents_.clear();
250 EventRecorder::Get().NotifyEventCacheEnd();
251 }
252 }
253
NotifyEvent(EventCategory category,int32_t eventType,const std::shared_ptr<std::unordered_map<std::string,std::string>> & eventParams)254 void EventController::NotifyEvent(EventCategory category, int32_t eventType,
255 const std::shared_ptr<std::unordered_map<std::string, std::string>>& eventParams)
256 {
257 CacheEventIfNeed(category, eventType, eventParams);
258 {
259 std::shared_lock<std::shared_mutex> lock(cacheLock_);
260 if (clientList_.empty()) {
261 return;
262 }
263 }
264 BackgroundTaskExecutor::GetInstance().PostTask([category, eventType, eventParams]() {
265 EventController::Get().NotifyEventSync(category, eventType, eventParams);
266 });
267 }
268
NotifyEventSync(EventCategory category,int32_t eventType,const std::shared_ptr<std::unordered_map<std::string,std::string>> & eventParams)269 void EventController::NotifyEventSync(EventCategory category, int32_t eventType,
270 const std::shared_ptr<std::unordered_map<std::string, std::string>>& eventParams)
271 {
272 std::shared_lock<std::shared_mutex> lock(cacheLock_);
273 for (auto&& client : clientList_) {
274 if (IsAllowNotify(client.config, category, eventType, eventParams)) {
275 client.observer->NotifyUIEvent(eventType, eventParams);
276 }
277 }
278 }
279
GetWebJsCodeList()280 std::vector<std::string> EventController::GetWebJsCodeList()
281 {
282 std::vector<std::string> codeList;
283 std::shared_lock<std::shared_mutex> lock(cacheLock_);
284 for (auto&& client : clientList_) {
285 if (client.config.IsCategoryEnable(static_cast<int32_t>(EventCategory::CATEGORY_WEB)) &&
286 !client.config.GetWebJsCode().empty()) {
287 codeList.emplace_back(client.config.GetWebJsCode());
288 }
289 }
290 return codeList;
291 }
292 } // namespace OHOS::Ace::Recorder
293