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