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