1 /*
2 * Copyright (c) 2021-2024 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
16 #include "notification_analytics_util.h"
17
18 #include "want_params_wrapper.h"
19 #include "string_wrapper.h"
20 #include "common_event_manager.h"
21 #include "common_event_support.h"
22 #include "common_event_publish_info.h"
23 #include "ans_convert_enum.h"
24 #include "ans_permission_def.h"
25 #include "in_process_call_wrapper.h"
26 #include "report_timer_info.h"
27 #include "time_service_client.h"
28 #include "nlohmann/json.hpp"
29 namespace OHOS {
30 namespace Notification {
31 constexpr char MESSAGE_DELIMITER = '#';
32 constexpr const int32_t PUBLISH_ERROR_EVENT_CODE = 0;
33 constexpr const int32_t DELETE_ERROR_EVENT_CODE = 5;
34 constexpr const int32_t MODIFY_ERROR_EVENT_CODE = 6;
35
36 constexpr const int32_t DEFAULT_ERROR_EVENT_COUNT = 5;
37 constexpr const int32_t DEFAULT_ERROR_EVENT_TIME = 60;
38 constexpr const int32_t MODIFY_ERROR_EVENT_COUNT = 6;
39 constexpr const int32_t MODIFY_ERROR_EVENT_TIME = 60;
40
41 constexpr const int32_t REPORT_CACHE_MAX_SIZE = 50;
42 constexpr const int32_t REPORT_CACHE_INTERVAL_TIME = 30;
43 const static std::string NOTIFICATION_EVENT_PUSH_AGENT = "notification.event.PUSH_AGENT";
44 static std::mutex reportFlowControlMutex_;
45 static std::map<int32_t, std::list<std::chrono::system_clock::time_point>> flowControlTimestampMap_ = {
46 {MODIFY_ERROR_EVENT_CODE, {}},
47 {PUBLISH_ERROR_EVENT_CODE, {}},
48 {DELETE_ERROR_EVENT_CODE, {}},
49 };
50
51 static std::mutex reportCacheMutex_;
52 static uint64_t reportTimerId = 0;
53 static std::list<ReportCache> reportCacheList;
54 static bool g_reportFlag = false;
55 static std::shared_ptr<ReportTimerInfo> reportTimeInfo = std::make_shared<ReportTimerInfo>();
56
HaMetaMessage(uint32_t sceneId, uint32_t branchId)57 HaMetaMessage::HaMetaMessage(uint32_t sceneId, uint32_t branchId)
58 : sceneId_(sceneId), branchId_(branchId)
59 {
60 }
61
NeedReport() const62 bool HaMetaMessage::NeedReport() const
63 {
64 if (errorCode_ == ERR_OK && checkfailed_) {
65 return false;
66 }
67 return true;
68 }
69
SceneId(uint32_t sceneId)70 HaMetaMessage& HaMetaMessage::SceneId(uint32_t sceneId)
71 {
72 sceneId_ = sceneId;
73 return *this;
74 }
75
BranchId(uint32_t branchId)76 HaMetaMessage& HaMetaMessage::BranchId(uint32_t branchId)
77 {
78 branchId_ = branchId;
79 return *this;
80 }
81
ErrorCode(uint32_t errorCode)82 HaMetaMessage& HaMetaMessage::ErrorCode(uint32_t errorCode)
83 {
84 errorCode_ = errorCode;
85 return *this;
86 }
87
Message(const std::string& message, bool print)88 HaMetaMessage& HaMetaMessage::Message(const std::string& message, bool print)
89 {
90 message_ = message;
91 if (print) {
92 ANSR_LOGE("%{public}s, %{public}d", message.c_str(), errorCode_);
93 }
94 return *this;
95 }
96
Checkfailed(bool checkfailed)97 HaMetaMessage& HaMetaMessage::Checkfailed(bool checkfailed)
98 {
99 checkfailed_ = checkfailed;
100 return *this;
101 }
102
BundleName(const std::string& bundleName)103 HaMetaMessage& HaMetaMessage::BundleName(const std::string& bundleName)
104 {
105 bundleName_ = bundleName;
106 return *this;
107 }
108
AgentBundleName(const std::string& agentBundleName)109 HaMetaMessage& HaMetaMessage::AgentBundleName(const std::string& agentBundleName)
110 {
111 agentBundleName_ = agentBundleName;
112 return *this;
113 }
114
TypeCode(int32_t typeCode)115 HaMetaMessage& HaMetaMessage::TypeCode(int32_t typeCode)
116 {
117 typeCode_ = typeCode;
118 return *this;
119 }
120
NotificationId(int32_t notificationId)121 HaMetaMessage& HaMetaMessage::NotificationId(int32_t notificationId)
122 {
123 notificationId_ = notificationId;
124 return *this;
125 }
126
GetMessage() const127 std::string HaMetaMessage::GetMessage() const
128 {
129 return message_;
130 }
131
SlotType(int32_t slotType)132 HaMetaMessage& HaMetaMessage::SlotType(int32_t slotType)
133 {
134 slotType_ = static_cast<uint32_t>(slotType);
135 return *this;
136 }
137
Build() const138 std::string HaMetaMessage::Build() const
139 {
140 return std::to_string(sceneId_) + MESSAGE_DELIMITER +
141 std::to_string(branchId_) + MESSAGE_DELIMITER + std::to_string(errorCode_) +
142 MESSAGE_DELIMITER + message_ + MESSAGE_DELIMITER;
143 }
144
ReportPublishFailedEvent(const sptr<NotificationRequest>& request, const HaMetaMessage& message)145 void NotificationAnalyticsUtil::ReportPublishFailedEvent(const sptr<NotificationRequest>& request,
146 const HaMetaMessage& message)
147 {
148 CommonNotificationEvent(request, PUBLISH_ERROR_EVENT_CODE, message);
149 }
150
ReportDeleteFailedEvent(const sptr<NotificationRequest>& request, HaMetaMessage& message)151 void NotificationAnalyticsUtil::ReportDeleteFailedEvent(const sptr<NotificationRequest>& request,
152 HaMetaMessage& message)
153 {
154 if (request == nullptr || !message.NeedReport()) {
155 ANS_LOGE("request is null %{public}d", message.NeedReport());
156 return;
157 }
158 std::shared_ptr<NotificationBundleOption> agentBundleNameOption = request->GetAgentBundle();
159 if (agentBundleNameOption != nullptr) {
160 std::string agentBundleName = agentBundleNameOption->GetBundleName();
161 if (!agentBundleName.empty()) {
162 message = message.AgentBundleName(agentBundleName);
163 }
164 }
165 CommonNotificationEvent(request, DELETE_ERROR_EVENT_CODE, message);
166 }
167
CommonNotificationEvent(const sptr<NotificationRequest>& request, int32_t eventCode, const HaMetaMessage& message)168 void NotificationAnalyticsUtil::CommonNotificationEvent(const sptr<NotificationRequest>& request,
169 int32_t eventCode, const HaMetaMessage& message)
170 {
171 if (request == nullptr) {
172 return;
173 }
174
175 if (!ReportFlowControl(eventCode)) {
176 ANS_LOGI("Publish event failed, eventCode:%{public}d, reason:%{public}s",
177 eventCode, message.Build().c_str());
178 return;
179 }
180 EventFwk::Want want;
181 std::string extraInfo = NotificationAnalyticsUtil::BuildExtraInfoWithReq(message, request);
182 NotificationAnalyticsUtil::SetCommonWant(want, message, extraInfo);
183
184 want.SetParam("typeCode", message.typeCode_);
185 IN_PROCESS_CALL_WITHOUT_RET(ReportNotificationEvent(
186 request, want, eventCode, message.Build()));
187 }
188
ReportNotificationEvent(const sptr<NotificationRequest>& request, EventFwk::Want want, int32_t eventCode, const std::string& reason)189 void NotificationAnalyticsUtil::ReportNotificationEvent(const sptr<NotificationRequest>& request,
190 EventFwk::Want want, int32_t eventCode, const std::string& reason)
191 {
192 NotificationNapi::SlotType slotType;
193 NotificationNapi::AnsEnumUtil::SlotTypeCToJS(
194 static_cast<NotificationConstant::SlotType>(request->GetSlotType()), slotType);
195 NotificationNapi::ContentType contentType;
196 NotificationNapi::AnsEnumUtil::ContentTypeCToJS(
197 static_cast<NotificationContent::Type>(request->GetNotificationType()), contentType);
198
199 want.SetParam("id", request->GetNotificationId());
200 want.SetParam("uid", request->GetOwnerUid());
201 want.SetParam("slotType", static_cast<int32_t>(slotType));
202 want.SetParam("contentType", std::to_string(static_cast<int32_t>(contentType)));
203
204 if (!request->GetCreatorBundleName().empty()) {
205 want.SetParam("agentBundleName", request->GetCreatorBundleName());
206 }
207 if (!request->GetOwnerBundleName().empty()) {
208 want.SetBundle(request->GetOwnerBundleName());
209 }
210 IN_PROCESS_CALL_WITHOUT_RET(AddListCache(want, eventCode));
211 }
212
ReportModifyEvent(const HaMetaMessage& message)213 void NotificationAnalyticsUtil::ReportModifyEvent(const HaMetaMessage& message)
214 {
215 if (!ReportFlowControl(MODIFY_ERROR_EVENT_CODE)) {
216 ANS_LOGI("Publish event failed, reason:%{public}s", message.Build().c_str());
217 return;
218 }
219 EventFwk::Want want;
220 std::string extraInfo = NotificationAnalyticsUtil::BuildExtraInfo(message);
221 NotificationAnalyticsUtil::SetCommonWant(want, message, extraInfo);
222
223 want.SetParam("slotType", static_cast<int32_t>(message.slotType_));
224 IN_PROCESS_CALL_WITHOUT_RET(AddListCache(want, MODIFY_ERROR_EVENT_CODE));
225 }
226
ReportDeleteFailedEvent(const HaMetaMessage& message)227 void NotificationAnalyticsUtil::ReportDeleteFailedEvent(const HaMetaMessage& message)
228 {
229 if (!ReportFlowControl(DELETE_ERROR_EVENT_CODE)) {
230 ANS_LOGI("Publish event failed, reason:%{public}s", message.Build().c_str());
231 return;
232 }
233 EventFwk::Want want;
234 std::string extraInfo = NotificationAnalyticsUtil::BuildExtraInfo(message);
235 NotificationAnalyticsUtil::SetCommonWant(want, message, extraInfo);
236
237 want.SetParam("agentBundleName", message.agentBundleName_);
238 want.SetParam("typeCode", message.typeCode_);
239 want.SetParam("id", message.notificationId_);
240
241 IN_PROCESS_CALL_WITHOUT_RET(AddListCache(want, DELETE_ERROR_EVENT_CODE));
242 }
243
ReportNotificationEvent(EventFwk::Want want, int32_t eventCode, const std::string& reason)244 void NotificationAnalyticsUtil::ReportNotificationEvent(EventFwk::Want want,
245 int32_t eventCode, const std::string& reason)
246 {
247 EventFwk::CommonEventPublishInfo publishInfo;
248 publishInfo.SetSubscriberPermissions({OHOS_PERMISSION_NOTIFICATION_AGENT_CONTROLLER});
249 EventFwk::CommonEventData commonData {want, eventCode, ""};
250 ANS_LOGD("Publish event success %{public}d, %{public}s", eventCode, reason.c_str());
251 if (!EventFwk::CommonEventManager::PublishCommonEvent(commonData, publishInfo)) {
252 ANS_LOGE("Publish event failed %{public}d, %{public}s", eventCode, reason.c_str());
253 }
254 }
255
ReportFlowControl(const int32_t reportType)256 bool NotificationAnalyticsUtil::ReportFlowControl(const int32_t reportType)
257 {
258 std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
259 std::lock_guard<std::mutex> lock(reportFlowControlMutex_);
260 auto iter = flowControlTimestampMap_.find(reportType);
261 if (iter == flowControlTimestampMap_.end()) {
262 return false;
263 }
264 auto& list = iter->second;
265 FlowControllerOption option = GetFlowOptionByType(reportType);
266 RemoveExpired(list, now, option.time);
267 int32_t size = static_cast<int32_t>(list.size());
268 int32_t count = option.count;
269 if (size >= count) {
270 return false;
271 }
272 list.push_back(now);
273 return true;
274 }
275
RemoveExpired(std::list<std::chrono::system_clock::time_point> &list, const std::chrono::system_clock::time_point &now, int32_t time)276 void NotificationAnalyticsUtil::RemoveExpired(std::list<std::chrono::system_clock::time_point> &list,
277 const std::chrono::system_clock::time_point &now, int32_t time)
278 {
279 auto iter = list.begin();
280 while (iter != list.end()) {
281 if (abs(now - *iter) > std::chrono::seconds(time)) {
282 iter = list.erase(iter);
283 } else {
284 break;
285 }
286 }
287 }
288
GetFlowOptionByType(const int32_t reportType)289 FlowControllerOption NotificationAnalyticsUtil::GetFlowOptionByType(const int32_t reportType)
290 {
291 FlowControllerOption option;
292 switch (reportType) {
293 case MODIFY_ERROR_EVENT_CODE:
294 option.count = MODIFY_ERROR_EVENT_COUNT;
295 option.time = MODIFY_ERROR_EVENT_TIME;
296 break;
297 default:
298 option.count = DEFAULT_ERROR_EVENT_COUNT;
299 option.time = DEFAULT_ERROR_EVENT_TIME;
300 break;
301 }
302 return option;
303 }
304
BuildExtraInfo(const HaMetaMessage& message)305 std::string NotificationAnalyticsUtil::BuildExtraInfo(const HaMetaMessage& message)
306 {
307 nlohmann::json reason;
308 reason["scene"] = message.sceneId_;
309 reason["branch"] = message.branchId_;
310 reason["innerErr"] = message.errorCode_;
311 reason["detail"] = message.message_;
312
313 auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
314 std::chrono::system_clock::now().time_since_epoch()).count();
315 reason["time"] = now;
316
317 std::shared_ptr<AAFwk::WantParams> extraInfo = std::make_shared<AAFwk::WantParams>();
318 extraInfo->SetParam("reason",
319 AAFwk::String::Box(reason.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace)));
320 AAFwk::WantParamWrapper wWrapper(*extraInfo);
321
322 return wWrapper.ToString();
323 }
324
BuildExtraInfoWithReq(const HaMetaMessage& message, const sptr<NotificationRequest>& request)325 std::string NotificationAnalyticsUtil::BuildExtraInfoWithReq(const HaMetaMessage& message,
326 const sptr<NotificationRequest>& request)
327 {
328 NotificationNapi::ContentType contentType;
329 NotificationNapi::AnsEnumUtil::ContentTypeCToJS(
330 static_cast<NotificationContent::Type>(request->GetNotificationType()), contentType);
331 nlohmann::json reason;
332 if (contentType == NotificationNapi::ContentType::NOTIFICATION_CONTENT_LIVE_VIEW) {
333 auto content = request->GetContent()->GetNotificationContent();
334 auto liveViewContent = std::static_pointer_cast<NotificationLiveViewContent>(content);
335 reason["status"] = static_cast<int32_t>(liveViewContent->GetLiveViewStatus());
336 }
337
338 reason["scene"] = message.sceneId_;
339 reason["branch"] = message.branchId_;
340 reason["innerErr"] = message.errorCode_;
341 reason["detail"] = message.message_;
342
343 auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
344 std::chrono::system_clock::now().time_since_epoch()).count();
345 reason["time"] = now;
346
347 std::shared_ptr<AAFwk::WantParams> extraInfo = nullptr;
348 if (request->GetUnifiedGroupInfo() != nullptr &&
349 request->GetUnifiedGroupInfo()->GetExtraInfo() != nullptr) {
350 extraInfo = request->GetUnifiedGroupInfo()->GetExtraInfo();
351 } else {
352 extraInfo = std::make_shared<AAFwk::WantParams>();
353 }
354
355 extraInfo->SetParam("reason",
356 AAFwk::String::Box(reason.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace)));
357 AAFwk::WantParamWrapper wWrapper(*extraInfo);
358
359 return wWrapper.ToString();
360 }
361
SetCommonWant(EventFwk::Want& want, const HaMetaMessage& message, std::string& extraInfo)362 void NotificationAnalyticsUtil::SetCommonWant(EventFwk::Want& want, const HaMetaMessage& message,
363 std::string& extraInfo)
364 {
365 want.SetBundle(message.bundleName_);
366 want.SetParam("extraInfo", extraInfo);
367 want.SetAction(NOTIFICATION_EVENT_PUSH_AGENT);
368 }
369
AddListCache(EventFwk::Want& want, int32_t eventCode)370 void NotificationAnalyticsUtil::AddListCache(EventFwk::Want& want, int32_t eventCode)
371 {
372 std::lock_guard<std::mutex> lock(reportCacheMutex_);
373 int32_t size = static_cast<int32_t>(reportCacheList.size());
374 if (size >= REPORT_CACHE_MAX_SIZE) {
375 ANS_LOGW("list size is max");
376 return;
377 }
378
379 if (reportTimerId == 0) {
380 sptr<MiscServices::TimeServiceClient> timer = MiscServices::TimeServiceClient::GetInstance();
381 if (timer == nullptr) {
382 ANS_LOGE("Failed to start timer due to get TimeServiceClient is null.");
383 return;
384 }
385 reportTimerId = timer->CreateTimer(reportTimeInfo);
386 }
387
388 ReportCache reportCache;
389 reportCache.want = want;
390 reportCache.eventCode = eventCode;
391 reportCacheList.push_back(reportCache);
392 if (!g_reportFlag) {
393 ExecuteCacheList();
394 }
395 }
396
ExecuteCacheList()397 void NotificationAnalyticsUtil::ExecuteCacheList()
398 {
399 if (reportCacheList.empty()) {
400 g_reportFlag = false;
401 ANS_LOGI("reportCacheList is end");
402 return;
403 }
404 auto reportCache = reportCacheList.front();
405 ReportCommonEvent(reportCache);
406 auto triggerFunc = [] {
407 std::lock_guard<std::mutex> lock(reportCacheMutex_);
408 NotificationAnalyticsUtil::ExecuteCacheList();
409 };
410 reportCacheList.pop_front();
411 reportTimeInfo->SetCallbackInfo(triggerFunc);
412 sptr<MiscServices::TimeServiceClient> timer = MiscServices::TimeServiceClient::GetInstance();
413 if (timer == nullptr || reportTimerId == 0) {
414 g_reportFlag = false;
415 ANS_LOGE("Failed to start timer due to get TimeServiceClient is null.");
416 return;
417 }
418 timer->StartTimer(reportTimerId, NotificationAnalyticsUtil::GetCurrentTime() +
419 REPORT_CACHE_INTERVAL_TIME * NotificationConstant::SECOND_TO_MS);
420 g_reportFlag = true;
421 }
422
ReportCommonEvent(const ReportCache& reportCache)423 void NotificationAnalyticsUtil::ReportCommonEvent(const ReportCache& reportCache)
424 {
425 EventFwk::CommonEventPublishInfo publishInfo;
426 publishInfo.SetSubscriberPermissions({OHOS_PERMISSION_NOTIFICATION_AGENT_CONTROLLER});
427 EventFwk::CommonEventData commonData {reportCache.want, reportCache.eventCode, ""};
428 if (!EventFwk::CommonEventManager::PublishCommonEvent(commonData, publishInfo)) {
429 ANS_LOGE("Publish event failed %{public}d", reportCache.eventCode);
430 }
431 }
432
GetCurrentTime()433 int64_t NotificationAnalyticsUtil::GetCurrentTime()
434 {
435 auto now = std::chrono::system_clock::now();
436 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
437 return duration.count();
438 }
439 } // namespace Notification
440 } // namespace OHOS
441