• 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/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