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