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