• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("slotType", static_cast<int32_t>(slotType));
207     want.SetParam("contentType", std::to_string(static_cast<int32_t>(contentType)));
208 
209     if (!request->GetCreatorBundleName().empty()) {
210         want.SetParam("agentBundleName", request->GetCreatorBundleName());
211     }
212     if (!request->GetOwnerBundleName().empty()) {
213         want.SetBundle(request->GetOwnerBundleName());
214     }
215     IN_PROCESS_CALL_WITHOUT_RET(AddListCache(want, eventCode));
216 }
217 
ReportModifyEvent(const HaMetaMessage & message)218 void NotificationAnalyticsUtil::ReportModifyEvent(const HaMetaMessage& message)
219 {
220     if (!ReportFlowControl(MODIFY_ERROR_EVENT_CODE)) {
221         ANS_LOGI("Publish event failed, reason:%{public}s", message.Build().c_str());
222         return;
223     }
224     EventFwk::Want want;
225     std::string extraInfo = NotificationAnalyticsUtil::BuildExtraInfo(message);
226     NotificationAnalyticsUtil::SetCommonWant(want, message, extraInfo);
227 
228     std::string bundle;
229     int32_t callingUid = IPCSkeleton::GetCallingUid();
230     std::shared_ptr<BundleManagerHelper> bundleManager = BundleManagerHelper::GetInstance();
231     if (bundleManager != nullptr) {
232         bundle = bundleManager->GetBundleNameByUid(callingUid);
233     }
234     want.SetBundle(bundle + "_" + std::to_string(callingUid));
235     want.SetParam("slotType", static_cast<int32_t>(message.slotType_));
236     IN_PROCESS_CALL_WITHOUT_RET(AddListCache(want, MODIFY_ERROR_EVENT_CODE));
237 }
238 
ReportDeleteFailedEvent(const HaMetaMessage & message)239 void NotificationAnalyticsUtil::ReportDeleteFailedEvent(const HaMetaMessage& message)
240 {
241     if (!ReportFlowControl(DELETE_ERROR_EVENT_CODE)) {
242         ANS_LOGI("Publish event failed, reason:%{public}s", message.Build().c_str());
243         return;
244     }
245     EventFwk::Want want;
246     std::string extraInfo = NotificationAnalyticsUtil::BuildExtraInfo(message);
247     NotificationAnalyticsUtil::SetCommonWant(want, message, extraInfo);
248 
249     want.SetParam("agentBundleName", message.agentBundleName_);
250     want.SetParam("typeCode", message.typeCode_);
251     want.SetParam("id", message.notificationId_);
252 
253     IN_PROCESS_CALL_WITHOUT_RET(AddListCache(want, DELETE_ERROR_EVENT_CODE));
254 }
255 
ReportNotificationEvent(EventFwk::Want want,int32_t eventCode,const std::string & reason)256 void NotificationAnalyticsUtil::ReportNotificationEvent(EventFwk::Want want,
257     int32_t eventCode, const std::string& reason)
258 {
259     EventFwk::CommonEventPublishInfo publishInfo;
260     publishInfo.SetSubscriberPermissions({OHOS_PERMISSION_NOTIFICATION_AGENT_CONTROLLER});
261     EventFwk::CommonEventData commonData {want, eventCode, ""};
262     ANS_LOGD("Publish event success %{public}d, %{public}s", eventCode, reason.c_str());
263     if (!EventFwk::CommonEventManager::PublishCommonEvent(commonData, publishInfo)) {
264         ANS_LOGE("Publish event failed %{public}d, %{public}s", eventCode, reason.c_str());
265     }
266 }
267 
ReportFlowControl(const int32_t reportType)268 bool NotificationAnalyticsUtil::ReportFlowControl(const int32_t reportType)
269 {
270     std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
271     std::lock_guard<std::mutex> lock(reportFlowControlMutex_);
272     auto iter = flowControlTimestampMap_.find(reportType);
273     if (iter == flowControlTimestampMap_.end()) {
274         return false;
275     }
276     auto& list = iter->second;
277     FlowControllerOption option = GetFlowOptionByType(reportType);
278     RemoveExpired(list, now, option.time);
279     int32_t size = list.size();
280     int32_t count = option.count;
281     if (size >= count) {
282         return false;
283     }
284     list.push_back(now);
285     return true;
286 }
287 
RemoveExpired(std::list<std::chrono::system_clock::time_point> & list,const std::chrono::system_clock::time_point & now,int32_t time)288 void NotificationAnalyticsUtil::RemoveExpired(std::list<std::chrono::system_clock::time_point> &list,
289     const std::chrono::system_clock::time_point &now, int32_t time)
290 {
291     auto iter = list.begin();
292     while (iter != list.end()) {
293         if (abs(now - *iter) > std::chrono::seconds(time)) {
294             iter = list.erase(iter);
295         } else {
296             break;
297         }
298     }
299 }
300 
GetFlowOptionByType(const int32_t reportType)301 FlowControllerOption NotificationAnalyticsUtil::GetFlowOptionByType(const int32_t reportType)
302 {
303     FlowControllerOption option;
304     switch (reportType) {
305         case MODIFY_ERROR_EVENT_CODE:
306             option.count = MODIFY_ERROR_EVENT_COUNT;
307             option.time = MODIFY_ERROR_EVENT_TIME;
308             break;
309         default:
310             option.count = DEFAULT_ERROR_EVENT_COUNT;
311             option.time = DEFAULT_ERROR_EVENT_TIME;
312             break;
313     }
314     return option;
315 }
316 
BuildExtraInfo(const HaMetaMessage & message)317 std::string NotificationAnalyticsUtil::BuildExtraInfo(const HaMetaMessage& message)
318 {
319     nlohmann::json reason;
320     reason["scene"] = message.sceneId_;
321     reason["branch"] = message.branchId_;
322     reason["innerErr"] = message.errorCode_;
323     reason["detail"] = message.message_;
324 
325     auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
326         std::chrono::system_clock::now().time_since_epoch()).count();
327     reason["time"] = now;
328 
329     std::shared_ptr<AAFwk::WantParams> extraInfo = std::make_shared<AAFwk::WantParams>();
330 
331     reason["detail"] = "";
332     int32_t reasonFixedSize =
333         static_cast<int32_t>(reason.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace).size());
334     int32_t leftSpace = REASON_MAX_LENGTH - reasonFixedSize;
335     if (leftSpace < 0) {
336         std::string basicInfo = std::to_string(message.sceneId_) + MESSAGE_DELIMITER +
337             std::to_string(message.branchId_) + MESSAGE_DELIMITER +
338             std::to_string(message.errorCode_) + MESSAGE_DELIMITER +
339             std::to_string(now) + " Reason fixed size exceeds limit";
340         extraInfo->SetParam("reason", AAFwk::String::Box(basicInfo));
341         ANS_LOGI("%{public}s", basicInfo.c_str());
342     } else {
343         reason["detail"] = message.message_.substr(0, leftSpace);
344         extraInfo->SetParam("reason",
345             AAFwk::String::Box(reason.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace)));
346     }
347 
348     AAFwk::WantParamWrapper wWrapper(*extraInfo);
349 
350     return wWrapper.ToString();
351 }
352 
BuildExtraInfoWithReq(const HaMetaMessage & message,const sptr<NotificationRequest> & request)353 std::string NotificationAnalyticsUtil::BuildExtraInfoWithReq(const HaMetaMessage& message,
354     const sptr<NotificationRequest>& request)
355 {
356     NotificationNapi::ContentType contentType;
357     NotificationNapi::AnsEnumUtil::ContentTypeCToJS(
358         static_cast<NotificationContent::Type>(request->GetNotificationType()), contentType);
359     nlohmann::json reason;
360     if (contentType == NotificationNapi::ContentType::NOTIFICATION_CONTENT_LIVE_VIEW) {
361         auto content = request->GetContent()->GetNotificationContent();
362         auto liveViewContent = std::static_pointer_cast<NotificationLiveViewContent>(content);
363         reason["status"] = static_cast<int32_t>(liveViewContent->GetLiveViewStatus());
364     }
365 
366     reason["scene"] = message.sceneId_;
367     reason["branch"] = message.branchId_;
368     reason["innerErr"] = message.errorCode_;
369     reason["detail"] = message.message_;
370 
371     auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
372         std::chrono::system_clock::now().time_since_epoch()).count();
373     reason["time"] = now;
374 
375     std::shared_ptr<AAFwk::WantParams> extraInfo = nullptr;
376     if (request->GetUnifiedGroupInfo() != nullptr &&
377         request->GetUnifiedGroupInfo()->GetExtraInfo() != nullptr) {
378         extraInfo = request->GetUnifiedGroupInfo()->GetExtraInfo();
379     } else {
380         extraInfo = std::make_shared<AAFwk::WantParams>();
381     }
382 
383     reason["detail"] = "";
384     int32_t reasonFixedSize =
385         static_cast<int32_t>(reason.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace).size());
386     int32_t leftSpace = REASON_MAX_LENGTH - reasonFixedSize;
387     if (leftSpace < 0) {
388         std::string basicInfo = std::to_string(message.sceneId_) + MESSAGE_DELIMITER +
389             std::to_string(message.branchId_) + MESSAGE_DELIMITER +
390             std::to_string(message.errorCode_) + MESSAGE_DELIMITER +
391             std::to_string(now) + " Reason fixed size exceeds limit";
392         extraInfo->SetParam("reason", AAFwk::String::Box(basicInfo));
393         ANS_LOGI("%{public}s", basicInfo.c_str());
394     } else {
395         reason["detail"] = message.message_.substr(0, leftSpace);
396         extraInfo->SetParam("reason",
397             AAFwk::String::Box(reason.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace)));
398     }
399 
400     AAFwk::WantParamWrapper wWrapper(*extraInfo);
401 
402     return wWrapper.ToString();
403 }
404 
SetCommonWant(EventFwk::Want & want,const HaMetaMessage & message,std::string & extraInfo)405 void NotificationAnalyticsUtil::SetCommonWant(EventFwk::Want& want, const HaMetaMessage& message,
406     std::string& extraInfo)
407 {
408     want.SetBundle(message.bundleName_);
409     want.SetParam("extraInfo", extraInfo);
410     want.SetAction(NOTIFICATION_EVENT_PUSH_AGENT);
411 }
412 
AddListCache(EventFwk::Want & want,int32_t eventCode)413 void NotificationAnalyticsUtil::AddListCache(EventFwk::Want& want, int32_t eventCode)
414 {
415     std::lock_guard<std::mutex> lock(reportCacheMutex_);
416     int32_t size = static_cast<int32_t>(reportCacheList.size());
417     if (size >= REPORT_CACHE_MAX_SIZE) {
418         ANS_LOGW("list size is max");
419         return;
420     }
421 
422     if (reportTimerId == 0) {
423         sptr<MiscServices::TimeServiceClient> timer = MiscServices::TimeServiceClient::GetInstance();
424         if (timer == nullptr) {
425             ANS_LOGE("Failed to start timer due to get TimeServiceClient is null.");
426             return;
427         }
428         reportTimerId = timer->CreateTimer(reportTimeInfo);
429     }
430 
431     ReportCache reportCache;
432     reportCache.want = want;
433     reportCache.eventCode = eventCode;
434     reportCacheList.push_back(reportCache);
435     if (!g_reportFlag) {
436         ExecuteCacheList();
437     }
438 }
439 
ExecuteCacheList()440 void NotificationAnalyticsUtil::ExecuteCacheList()
441 {
442     if (reportCacheList.empty()) {
443         g_reportFlag = false;
444         ANS_LOGI("reportCacheList is end");
445         return;
446     }
447     auto reportCache = reportCacheList.front();
448     ReportCommonEvent(reportCache);
449     auto triggerFunc = [] {
450         std::lock_guard<std::mutex> lock(reportCacheMutex_);
451         NotificationAnalyticsUtil::ExecuteCacheList();
452     };
453     reportCacheList.pop_front();
454     reportTimeInfo->SetCallbackInfo(triggerFunc);
455     sptr<MiscServices::TimeServiceClient> timer = MiscServices::TimeServiceClient::GetInstance();
456     if (timer == nullptr || reportTimerId == 0) {
457         g_reportFlag = false;
458         ANS_LOGE("Failed to start timer due to get TimeServiceClient is null.");
459         return;
460     }
461     timer->StartTimer(reportTimerId, NotificationAnalyticsUtil::GetCurrentTime() +
462         REPORT_CACHE_INTERVAL_TIME * NotificationConstant::SECOND_TO_MS);
463     g_reportFlag = true;
464 }
465 
ReportCommonEvent(const ReportCache & reportCache)466 void NotificationAnalyticsUtil::ReportCommonEvent(const ReportCache& reportCache)
467 {
468     EventFwk::CommonEventPublishInfo publishInfo;
469     publishInfo.SetSubscriberPermissions({OHOS_PERMISSION_NOTIFICATION_AGENT_CONTROLLER});
470     EventFwk::CommonEventData commonData {reportCache.want, reportCache.eventCode, ""};
471     if (!EventFwk::CommonEventManager::PublishCommonEvent(commonData, publishInfo)) {
472         ANS_LOGE("Publish event failed %{public}d", reportCache.eventCode);
473     }
474 }
475 
GetCurrentTime()476 int64_t NotificationAnalyticsUtil::GetCurrentTime()
477 {
478     auto now = std::chrono::system_clock::now();
479     auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
480     return duration.count();
481 }
482 } // namespace Notification
483 } // namespace OHOS
484