1 /*
2  * Copyright (C) 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 "nfc_notification.h"
17 
18 #include <map>
19 
20 #include "cJSON.h"
21 #include "file_ex.h"
22 #include "locale_config.h"
23 #include "locale_info.h"
24 #include "loghelper.h"
25 #include "nfc_sdk_common.h"
26 #include "securec.h"
27 #include "want_agent_helper.h"
28 #include "want_agent_info.h"
29 
30 #ifdef DEBUG
31 #undef DEBUG
32 #endif
33 #include "notification_helper.h"
34 
35 namespace OHOS {
36 namespace NFC {
37 namespace TAG {
38 static std::string g_sysLanguage = "";
39 static std::map<std::string, std::string> g_resourceMap;
40 static std::mutex g_callbackMutex {};
41 static NfcNtfCallback g_ntfCallback = nullptr;
42 
43 class NfcNotificationSubscriber : public Notification::NotificationSubscriber {
OnConnected()44     void OnConnected() {}
OnDisconnected()45     void OnDisconnected() {}
OnUpdate(const std::shared_ptr<Notification::NotificationSortingMap> &sortingMap)46     void OnUpdate(const std::shared_ptr<Notification::NotificationSortingMap> &sortingMap) {}
OnDoNotDisturbDateChange(const std::shared_ptr<Notification::NotificationDoNotDisturbDate> &date)47     void OnDoNotDisturbDateChange(const std::shared_ptr<Notification::NotificationDoNotDisturbDate> &date) {}
OnEnabledNotificationChanged( const std::shared_ptr<Notification::EnabledNotificationCallbackData> &callbackData)48     void OnEnabledNotificationChanged(
49         const std::shared_ptr<Notification::EnabledNotificationCallbackData> &callbackData) {}
OnDied()50     void OnDied() {}
OnCanceled(const std::shared_ptr<OHOS::Notification::Notification> &request, const std::shared_ptr<Notification::NotificationSortingMap> &sortingMap, int deleteReason)51     void OnCanceled(const std::shared_ptr<OHOS::Notification::Notification> &request,
52         const std::shared_ptr<Notification::NotificationSortingMap> &sortingMap, int deleteReason)
53     {
54         int creatorUid = request->GetUid();
55         int notificationId = request->GetId();
56         InfoLog("Oncanceled, creatorUid = %{public}d, notificationId = %{public}d, deleteReason = %{public}d",
57             creatorUid, notificationId, deleteReason);
58 
59         std::lock_guard<std::mutex> lock(g_callbackMutex);
60         if (deleteReason == Notification::NotificationConstant::CLICK_REASON_DELETE && g_ntfCallback) {
61             g_ntfCallback(notificationId % OHOS::NFC::TAG::NTF_COUNT_CONSTANT);
62         }
63     }
OnConsumed(const std::shared_ptr<OHOS::Notification::Notification> &notification, const std::shared_ptr<Notification::NotificationSortingMap> &sortingMap)64     void OnConsumed(const std::shared_ptr<OHOS::Notification::Notification> &notification,
65         const std::shared_ptr<Notification::NotificationSortingMap> &sortingMap) {}
OnBadgeChanged(const std::shared_ptr<Notification::BadgeNumberCallbackData> &badgeData)66     void OnBadgeChanged(const std::shared_ptr<Notification::BadgeNumberCallbackData> &badgeData) {}
OnBadgeEnabledChanged(const sptr<Notification::EnabledNotificationCallbackData> &callbackData)67     void OnBadgeEnabledChanged(const sptr<Notification::EnabledNotificationCallbackData> &callbackData) {}
OnBatchCanceled(const std::vector<std::shared_ptr<OHOS::Notification::Notification>> &requestList, const std::shared_ptr<Notification::NotificationSortingMap> &sortingMap, int32_t deleteReason)68     void OnBatchCanceled(const std::vector<std::shared_ptr<OHOS::Notification::Notification>> &requestList,
69         const std::shared_ptr<Notification::NotificationSortingMap> &sortingMap, int32_t deleteReason) {}
70 };
71 
72 static std::shared_ptr<NfcNotificationSubscriber> g_notificationSubscriber
73     = std::make_shared<NfcNotificationSubscriber>();
74 
UpdateResourceMap(const std::string &resourcePath)75 static void UpdateResourceMap(const std::string &resourcePath)
76 {
77     InfoLog("Reading resource string from json config.");
78 
79     std::string content;
80     LoadStringFromFile(resourcePath, content);
81     cJSON *json = cJSON_Parse(content.c_str());
82     if (json == nullptr) {
83         ErrorLog("json nullptr.");
84         return;
85     }
86 
87     cJSON *resJson = cJSON_GetObjectItemCaseSensitive(json, KEY_STRING);
88     if (resJson == nullptr || cJSON_GetArraySize(resJson) > MAX_RES_VEC_LEN) {
89         ErrorLog("fail to parse res json");
90         cJSON_Delete(json);
91         return;
92     }
93 
94     g_resourceMap.clear();
95     cJSON *resJsonEach = nullptr;
96     cJSON_ArrayForEach(resJsonEach, resJson) {
97         cJSON *key = cJSON_GetObjectItemCaseSensitive(resJsonEach, KEY_NAME);
98         if (key == nullptr || !cJSON_IsString(key)) {
99             ErrorLog("json param not string");
100             cJSON_Delete(json);
101             return;
102         }
103 
104         cJSON *value = cJSON_GetObjectItemCaseSensitive(resJsonEach, KEY_VALUE);
105         if (value == nullptr || !cJSON_IsString(value)) {
106             ErrorLog("json param not string");
107             cJSON_Delete(json);
108             return;
109         }
110 
111         g_resourceMap.insert(std::pair<std::string, std::string>(key->valuestring, value->valuestring));
112     }
113     cJSON_Delete(json);
114 }
115 
UpdateResourceMapByLanguage()116 static void UpdateResourceMapByLanguage()
117 {
118     std::string curSysLanguage = "zh";
119     OHOS::Global::I18n::LocaleInfo locale(Global::I18n::LocaleConfig::GetSystemLocale());
120     curSysLanguage = locale.GetLanguage();
121     if (g_sysLanguage == curSysLanguage) {
122         DebugLog("same language environment, no need to update resource map.");
123         return;
124     }
125 
126     InfoLog("current system language[%{public}s] changes, should update resource map", curSysLanguage.c_str());
127     g_sysLanguage = curSysLanguage;
128 
129     if (g_sysLanguage == "en") {
130         UpdateResourceMap(NFC_RES_EN_JSON_FILEPATH);
131     } else {
132         UpdateResourceMap(NFC_RES_DEFAULT_JSON_FILEPATH);
133     }
134 }
135 
GetTrafficCardNotificationText(const std::string &cardName, int balance)136 static std::string GetTrafficCardNotificationText(const std::string &cardName, int balance)
137 {
138     char buf[MAX_BUFF_LEN] = {0};
139     int ret = sprintf_s(buf, MAX_BUFF_LEN, g_resourceMap[KEY_TRANSPORT_CARD_NTF_TEXT].c_str(),
140         g_resourceMap[cardName].c_str(), static_cast<float>(balance) / NFC_UNIT_CHANGE_CONSTANT);
141     if (ret <= 0) {
142         ErrorLog("sprintf_s failed, ret[%{public}d]", ret);
143         return "";
144     }
145 
146     return std::string(buf);
147 }
148 
149 #ifdef NDEF_WIFI_ENABLED
GetWifiNotificationText(const std::string &ssid)150 static std::string GetWifiNotificationText(const std::string &ssid)
151 {
152     char buf[MAX_BUFF_LEN] = {0};
153     int ret = sprintf_s(buf, MAX_BUFF_LEN, g_resourceMap[KEY_NFC_WIFI_NTF_TEXT].c_str(), ssid.c_str());
154     if (ret <= 0) {
155         ErrorLog("sprintf_s failed, ret[%{public}d]", ret);
156         return "";
157     }
158 
159     return std::string(buf);
160 }
161 #endif
162 
163 #ifdef NDEF_BT_ENABLED
GetBtNotificationText(const std::string &name)164 static std::string GetBtNotificationText(const std::string &name)
165 {
166     char buf[MAX_BUFF_LEN] = {0};
167     int ret = sprintf_s(buf, MAX_BUFF_LEN, g_resourceMap[KEY_NFC_BT_NTF_TEXT].c_str(), name.c_str());
168     if (ret <= 0) {
169         ErrorLog("sprintf_s failed, ret[%{public}d]", ret);
170         return "";
171     }
172 
173     return std::string(buf);
174 }
175 #endif
176 
SetTitleAndTextForOtherNotificationId(int notificationId, std::shared_ptr<Notification::NotificationNormalContent> nfcContent, const std::string &name, int balance)177 static bool SetTitleAndTextForOtherNotificationId(int notificationId,
178     std::shared_ptr<Notification::NotificationNormalContent> nfcContent, const std::string &name, int balance)
179 {
180     switch (notificationId) {
181         case NFC_TAG_DEFAULT_NTF_ID:
182             if (g_resourceMap.find(KEY_TAG_DEFAULT_NTF_TITLE) != g_resourceMap.end() &&
183                 g_resourceMap.find(KEY_TAG_DEFAULT_NTF_TEXT) != g_resourceMap.end()) {
184                 nfcContent->SetTitle(g_resourceMap[KEY_TAG_DEFAULT_NTF_TITLE]);
185                 nfcContent->SetText(g_resourceMap[KEY_TAG_DEFAULT_NTF_TEXT]);
186             }
187             break;
188         case NFC_BROWSER_NOTIFICATION_ID:
189             if (g_resourceMap.find(KEY_TAG_DEFAULT_NTF_TITLE) != g_resourceMap.end() &&
190                 g_resourceMap.find(NFC_OPEN_LINK_TEXT_HEAD) != g_resourceMap.end()) {
191                 nfcContent->SetTitle(g_resourceMap[KEY_TAG_DEFAULT_NTF_TITLE]);
192                 nfcContent->SetText(g_resourceMap[NFC_OPEN_LINK_TEXT_HEAD] + name);
193             }
194             break;
195         case NFC_HCE_AID_CONFLICTED_ID:
196             if (g_resourceMap.find(KEY_HCE_AID_CONFLICTED_TITLE) != g_resourceMap.end() &&
197                 g_resourceMap.find(KEY_HCE_AID_CONFLICTED_TEXT) != g_resourceMap.end()) {
198                 nfcContent->SetTitle(g_resourceMap[KEY_HCE_AID_CONFLICTED_TITLE]);
199                 nfcContent->SetText(g_resourceMap[KEY_HCE_AID_CONFLICTED_TEXT]);
200             }
201             break;
202         case NFC_NO_HAP_SUPPORTED_NOTIFICATION_ID:
203             if (g_resourceMap.find(KEY_NO_HAP_TITLE) != g_resourceMap.end() &&
204                 g_resourceMap.find(KEY_NO_HAP_TEXT) != g_resourceMap.end()) {
205                 nfcContent->SetTitle(g_resourceMap[KEY_NO_HAP_TITLE]);
206                 nfcContent->SetText(g_resourceMap[KEY_NO_HAP_TEXT]);
207             }
208             break;
209         default:
210             WarnLog("unknown notification ID");
211             return false;
212     }
213     return true;
214 }
215 
SetTitleAndText(int notificationId, std::shared_ptr<Notification::NotificationNormalContent> nfcContent, const std::string &name, int balance)216 static bool SetTitleAndText(int notificationId,
217     std::shared_ptr<Notification::NotificationNormalContent> nfcContent, const std::string &name, int balance)
218 {
219     if (nfcContent == nullptr) {
220         ErrorLog("notification normal content nullptr");
221         return false;
222     }
223     UpdateResourceMapByLanguage();
224 
225     switch (notificationId) {
226         case NFC_TRANSPORT_CARD_NOTIFICATION_ID:
227             if (g_resourceMap.find(KEY_TRANSPORT_CARD_NTF_TITLE) != g_resourceMap.end() &&
228                 g_resourceMap.find(KEY_TRANSPORT_CARD_NTF_TEXT) != g_resourceMap.end() &&
229                 g_resourceMap.find(name) != g_resourceMap.end()) {
230                 nfcContent->SetTitle(g_resourceMap[KEY_TRANSPORT_CARD_NTF_TITLE]);
231                 nfcContent->SetText(GetTrafficCardNotificationText(name, balance));
232             }
233             break;
234         case NFC_WIFI_NOTIFICATION_ID:
235 #ifdef NDEF_WIFI_ENABLED
236             if (g_resourceMap.find(KEY_NFC_WIFI_NTF_TITLE) != g_resourceMap.end() &&
237                 g_resourceMap.find(KEY_NFC_WIFI_NTF_TEXT) != g_resourceMap.end()) {
238                 nfcContent->SetTitle(g_resourceMap[KEY_NFC_WIFI_NTF_TITLE]);
239                 nfcContent->SetText(GetWifiNotificationText(name));
240             }
241             break;
242 #else
243             ErrorLog("nfc wifi notification not supported");
244             return false;
245 #endif
246         case NFC_BT_NOTIFICATION_ID:
247 #ifdef NDEF_BT_ENABLED
248             if (g_resourceMap.find(KEY_NFC_BT_NTF_TITLE) != g_resourceMap.end() &&
249                 g_resourceMap.find(KEY_NFC_BT_NTF_TEXT) != g_resourceMap.end()) {
250                 nfcContent->SetTitle(g_resourceMap[KEY_NFC_BT_NTF_TITLE]);
251                 nfcContent->SetText(GetBtNotificationText(name));
252             }
253             break;
254 #else
255             ErrorLog("nfc bt notification not supported");
256             return false;
257 #endif
258         default:
259             return SetTitleAndTextForOtherNotificationId(notificationId, nfcContent, name, balance);
260     }
261     return true;
262 }
263 
GetButtonName(int notificationId)264 static std::string GetButtonName(int notificationId)
265 {
266     switch (notificationId) {
267         case NFC_BT_NOTIFICATION_ID:
268             if (g_resourceMap.find(KEY_NFC_BT_BUTTON_NAME) != g_resourceMap.end()) {
269                 return g_resourceMap[KEY_NFC_BT_BUTTON_NAME];
270             }
271             return "";
272         case NFC_WIFI_NOTIFICATION_ID:
273             if (g_resourceMap.find(KEY_NFC_WIFI_BUTTON_NAME) != g_resourceMap.end()) {
274                 return g_resourceMap[KEY_NFC_WIFI_BUTTON_NAME];
275             }
276             return "";
277         case NFC_BROWSER_NOTIFICATION_ID:
278             if (g_resourceMap.find(NFC_OPEN_LINK_BUTTON_NAME) != g_resourceMap.end()) {
279                 return g_resourceMap[NFC_OPEN_LINK_BUTTON_NAME];
280             }
281             return "";
282         case NFC_NO_HAP_SUPPORTED_NOTIFICATION_ID:
283             if (g_resourceMap.find(KEY_NO_HAP_BUTTON_NAME) != g_resourceMap.end()) {
284                 return g_resourceMap[KEY_NO_HAP_BUTTON_NAME];
285             }
286             return "";
287         default:
288             if (g_resourceMap.find(KEY_ACTION_BUTTON_NAME) != g_resourceMap.end()) {
289                 return g_resourceMap[KEY_ACTION_BUTTON_NAME];
290             }
291             return "";
292     }
293 }
294 
SetActionButton(const std::string& buttonName, Notification::NotificationRequest& request)295 static void SetActionButton(const std::string& buttonName, Notification::NotificationRequest& request)
296 {
297     auto want = std::make_shared<AAFwk::Want>();
298     std::vector<std::shared_ptr<AAFwk::Want>> wants;
299     wants.push_back(want);
300     std::vector<AbilityRuntime::WantAgent::WantAgentConstant::Flags> flags;
301     flags.push_back(AbilityRuntime::WantAgent::WantAgentConstant::Flags::CONSTANT_FLAG);
302     AbilityRuntime::WantAgent::WantAgentInfo wantAgentInfo(
303         0, AbilityRuntime::WantAgent::WantAgentConstant::OperationType::UNKNOWN_TYPE,
304         flags, wants, nullptr
305     );
306     auto wantAgentDeal = AbilityRuntime::WantAgent::WantAgentHelper::GetWantAgent(wantAgentInfo);
307     std::shared_ptr<Notification::NotificationActionButton> actionButtonDeal =
308         Notification::NotificationActionButton::Create(nullptr, buttonName, wantAgentDeal);
309     if (actionButtonDeal == nullptr) {
310         ErrorLog("get notification actionButton nullptr");
311         return;
312     }
313     request.AddActionButton(actionButtonDeal);
314 }
315 
GetAutoDeleteTime()316 static int64_t GetAutoDeleteTime()
317 {
318     auto now = std::chrono::system_clock::now();
319     auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
320     return duration.count() + NTF_AUTO_DELETE_TIME;
321 }
322 
SetBasicOption(Notification::NotificationRequest &request)323 static void SetBasicOption(Notification::NotificationRequest &request)
324 {
325     request.SetCreatorUid(KITS::NFC_MANAGER_SYS_ABILITY_ID);
326     request.SetCreatorBundleName(KITS::NFC_MANAGER_SYS_ABILITY_NAME);
327     request.SetAutoDeletedTime(GetAutoDeleteTime());
328     request.SetTapDismissed(true);
329     request.SetSlotType(OHOS::Notification::NotificationConstant::SlotType::SOCIAL_COMMUNICATION);
330     request.SetNotificationControlFlags(NFC_NTF_CONTROL_FLAG);
331 }
332 
GetPixelMap(const std::string &path)333 void NfcNotification::GetPixelMap(const std::string &path)
334 {
335     if (nfcIconPixelMap_ != nullptr) {
336         InfoLog("nfc icon pixel map already exists.");
337         return;
338     }
339 
340     if (!std::filesystem::exists(path)) {
341         ErrorLog("nfc icon file path not exists.");
342         nfcIconPixelMap_ = nullptr;
343         return;
344     }
345     uint32_t errorCode = 0;
346     Media::SourceOptions opts;
347     opts.formatHint = "image/png";
348     std::unique_ptr<Media::ImageSource> imageSource = Media::ImageSource::CreateImageSource(path, opts, errorCode);
349     if (imageSource == nullptr) {
350         ErrorLog("imageSource nullptr");
351         nfcIconPixelMap_ = nullptr;
352         return;
353     }
354     Media::DecodeOptions decodeOpts;
355     std::unique_ptr<Media::PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, errorCode);
356     nfcIconPixelMap_ = std::move(pixelMap);
357 }
358 
GetInstance()359 NfcNotification& NfcNotification::GetInstance()
360 {
361     static NfcNotification instance;
362     return instance;
363 }
364 
NfcNotification()365 NfcNotification::NfcNotification()
366 {
367     InfoLog("NfcNotification constructor enter.");
368     std::lock_guard<std::mutex> lock(mutex_);
369     // only need to subscribe notification once
370     int result = Notification::NotificationHelper::SubscribeNotification(*g_notificationSubscriber);
371     if (result != ERR_OK) {
372         ErrorLog("fail to subscribe notification");
373     }
374     UpdateResourceMapByLanguage();
375     // initialize the vector with the length of (NFC_NTF_END - NFC_TAG_DEFAULT_NTF_ID)
376     tagNtfCountVec_.resize(NFC_NTF_END - NFC_TAG_DEFAULT_NTF_ID);
377 }
378 
~NfcNotification()379 NfcNotification::~NfcNotification()
380 {
381     InfoLog("NfcNotification destructor enter.");
382     // no operation to unsubscribe notification
383 }
384 
PublishNfcNotification(int notificationId, const std::string &name, int balance)385 void NfcNotification::PublishNfcNotification(int notificationId, const std::string &name, int balance)
386 {
387     if (notificationId >= NFC_NTF_END || notificationId < NFC_TAG_DEFAULT_NTF_ID) {
388         ErrorLog("invalid notification id.");
389         return;
390     }
391     std::shared_ptr<Notification::NotificationNormalContent> nfcContent =
392         std::make_shared<Notification::NotificationNormalContent>();
393     if (nfcContent == nullptr) {
394         ErrorLog("get notification normal content nullptr");
395         return;
396     }
397     std::lock_guard<std::mutex> lock(mutex_);
398     Notification::NotificationBundleOption bundle(KITS::NFC_MANAGER_SYS_ABILITY_NAME, KITS::NFC_MANAGER_SYS_ABILITY_ID);
399     int lastNtfId = (tagNtfCountVec_[notificationId - NFC_TAG_DEFAULT_NTF_ID]++) * NTF_COUNT_CONSTANT + notificationId;
400     if (tagNtfCountVec_[notificationId - NFC_TAG_DEFAULT_NTF_ID] >= NFC_MAX_NTF_COUNT) {
401         tagNtfCountVec_[notificationId - NFC_TAG_DEFAULT_NTF_ID] = 0;
402     }
403     int currentNtfId = (tagNtfCountVec_[notificationId - NFC_TAG_DEFAULT_NTF_ID]) * NTF_COUNT_CONSTANT + notificationId;
404     int ret = Notification::NotificationHelper::CancelAsBundle(bundle, lastNtfId);
405     // ret value 67108880 represents the notification does not exist
406     InfoLog("Cancel ntf result[%{public}d], last id[%{public}d], current id[%{public}d]", ret, lastNtfId, currentNtfId);
407 
408     if (!SetTitleAndText(notificationId, nfcContent, name, balance)) {
409         ErrorLog("error setting title and text");
410         return;
411     }
412 
413     std::shared_ptr<Notification::NotificationContent> content =
414         std::make_shared<Notification::NotificationContent>(nfcContent);
415     if (content == nullptr) {
416         ErrorLog("get notification content nullptr");
417         return;
418     }
419 
420     Notification::NotificationRequest request;
421     SetBasicOption(request);
422     request.SetNotificationId(currentNtfId);
423     request.SetContent(content);
424 
425     GetPixelMap(NFC_ICON_PATH);
426     if (nfcIconPixelMap_ != nullptr) {
427         request.SetLittleIcon(nfcIconPixelMap_);
428         request.SetBadgeIconStyle(Notification::NotificationRequest::BadgeStyle::LITTLE);
429     }
430 
431     std::string buttonName = GetButtonName(notificationId);
432     if (!buttonName.empty()) {
433         SetActionButton(buttonName, request);
434     }
435 
436     Notification::NotificationHelper::SetNotificationSlotFlagsAsBundle(bundle, NFC_SLOT_CONTROL_FLAG);
437     ret = Notification::NotificationHelper::PublishNotification(request);
438     InfoLog("NFC service publish notification result = %{public}d", ret);
439 }
440 
RegNotificationCallback(NfcNtfCallback callback)441 void NfcNotification::RegNotificationCallback(NfcNtfCallback callback)
442 {
443     std::lock_guard<std::mutex> lock(g_callbackMutex);
444     g_ntfCallback = callback;
445 }
446 }  // namespace TAG
447 }  // namespace NFC
448 }  // namespace OHOS
449 
RegNotificationCallback(NfcNtfCallback callback)450 void RegNotificationCallback(NfcNtfCallback callback)
451 {
452     OHOS::NFC::TAG::NfcNotification::GetInstance().RegNotificationCallback(callback);
453 }
454 
PublishNfcNotification(int notificationId, const std::string &name, int balance)455 void PublishNfcNotification(int notificationId, const std::string &name, int balance)
456 {
457     InfoLog("Publishing nfc tag notification, id [%{public}d]", notificationId);
458     OHOS::NFC::TAG::NfcNotification::GetInstance().PublishNfcNotification(notificationId, name, balance);
459 }