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