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 * WITHstrNum 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 "net_stats_notification.h"
17 #include "net_stats_service.h"
18
19 #include <string>
20 #include <map>
21 #include <vector>
22 #include <sstream>
23 #include <iomanip>
24
25 #include "cJSON.h"
26 #include "file_ex.h"
27 #include "locale_config.h"
28 #include "locale_info.h"
29 #include "locale_matcher.h"
30 #include "securec.h"
31 #include "want_agent_helper.h"
32 #include "want_agent_info.h"
33
34 #include "image_source.h"
35 #include "pixel_map.h"
36 #include "notification_normal_content.h"
37 #include "net_mgr_log_wrapper.h"
38 #include "net_stats_utils.h"
39 #include "core_service_client.h"
40
41 namespace OHOS {
42 namespace NetManagerStandard {
43
44 static const int NTF_AUTO_DELETE_TIME = 10000;
45
46 // static const int COMM_NETSYS_NATIVE_SYS_ABILITY_ID = 1158
47
48 static const int32_t SLOT_ID_00 = 0;
49 static const int32_t CARD_ID_01 = 1;
50 static const int32_t CARD_ID_02 = 2;
51
52 static const int32_t UNIT_CONVERT_1024 = 1024;
53 static const int32_t TWO_PRECISION = 2;
54
55 static const int32_t TWO_CHAR = 2;
56
57 // keys in json file
58 static constexpr const char *KEY_STRING = "string";
59 static constexpr const char *KEY_NAME = "name";
60 static constexpr const char *KEY_VALUE = "value";
61 static constexpr const char *KEY_NETWORK_MONTH_LIMIT_SIMGLE_TITLE = "netstats_excess_monthlimit_notofication_title";
62 static constexpr const char *KEY_NETWORK_MONTH_MARK_SIMGLE_TITLE = "netstats_excess_monthmark_notofication_title";
63 static constexpr const char *KEY_NETWORK_DAY_MARK_SIMGLE_TITLE = "netstats_excess_daymark_notofication_title";
64
65 static constexpr const char *KEY_NETWORK_MONTH_LIMIT_DUAL_TITLE = "netstats_excess_monthlimit_notofication_title_sub";
66 static constexpr const char *KEY_NETWORK_MONTH_MARK_DUAL_TITLE = "netstats_excess_monthmark_notofication_title_sub";
67 static constexpr const char *KEY_NETWORK_DAY_MARK_DUAL_TITLE = "netstats_excess_daymark_notofication_title_sub";
68
69 static constexpr const char *KEY_MONTH_LIMIT_TEXT = "netstats_month_limit_message";
70 static constexpr const char *KEY_MONTH_NOTIFY_TEXT = "netstats_month_notify_message";
71 static constexpr const char *KEY_DAILY_NOTIFY_TEXT = "netstats_daily_notify_message";
72
73 // NOTE: icon and json path must be absolute path
74 // all locales are listed at: global_i18n-master\global_i18n-master\frameworks\intl\etc\supported_locales.xml
75 static constexpr const char *NETWORK_ICON_PATH = "//system/etc/netmanager_base/resources/network_ic.png";
76 static constexpr const char *DEFAULT_LANGUAGE_NAME_EN = "base";
77 static constexpr const char *LOCALE_TO_RESOURCE_PATH =
78 "//system/etc/netmanager_base/resources/locale_to_resourcePath.json";
79 static constexpr const char *LANGUAGE_RESOURCE_PARENT_PATH =
80 "//system/etc/netmanager_base/resources/";
81 static constexpr const char *LANGUAGE_RESOURCE_CHILD_PATH = "/element/string.json";
82
83 static std::mutex g_callbackMutex {};
84 static NetMgrStatsLimitNtfCallback g_NetMgrStatsLimitNtfCallback = nullptr;
85
ParseJSONFile(const std::string & filePath,std::map<std::string,std::string> & container)86 void NetMgrNetStatsLimitNotification::ParseJSONFile(
87 const std::string& filePath, std::map<std::string, std::string>& container)
88 {
89 std::string content;
90 LoadStringFromFile(filePath, content);
91
92 cJSON *json = cJSON_Parse(content.c_str());
93 if (json == nullptr) {
94 NETMGR_LOG_I("ParseJSONFile: json null. filepath = %{public}s", filePath.c_str());
95 return;
96 }
97
98 cJSON *resJson = cJSON_GetObjectItemCaseSensitive(json, KEY_STRING);
99
100 if (resJson == nullptr) {
101 NETMGR_LOG_I("ParseJSONFile: resJson null. filepath = %{public}s", filePath.c_str());
102 } else {
103 container.clear();
104 cJSON *resJsonEach = nullptr;
105 cJSON_ArrayForEach(resJsonEach, resJson) {
106 cJSON *key = cJSON_GetObjectItemCaseSensitive(resJsonEach, KEY_NAME);
107 cJSON *value = cJSON_GetObjectItemCaseSensitive(resJsonEach, KEY_VALUE);
108 container.insert(std::pair<std::string, std::string>(key->valuestring, value->valuestring));
109 }
110 }
111
112 cJSON_Delete(json);
113 }
114
UpdateResourceMap()115 void NetMgrNetStatsLimitNotification::UpdateResourceMap()
116 {
117 OHOS::Global::I18n::LocaleInfo locale(Global::I18n::LocaleConfig::GetSystemLocale());
118 std::string curBaseName = locale.GetBaseName();
119 if (localeBaseName == curBaseName) {
120 return;
121 }
122
123 NETMGR_LOG_I("UpdateResourceMap: change from %{public}s to %{public}s",
124 localeBaseName.c_str(), curBaseName.c_str());
125 localeBaseName = curBaseName;
126
127 std::string languagePath = DEFAULT_LANGUAGE_NAME_EN;
128 if (languageMap.find(localeBaseName) != languageMap.end()) {
129 languagePath = languageMap[localeBaseName];
130 } else {
131 for (auto& eachPair : languageMap) {
132 OHOS::Global::I18n::LocaleInfo eachLocale(eachPair.first);
133 if (OHOS::Global::I18n::LocaleMatcher::Match(&locale, &eachLocale)) {
134 languagePath = eachPair.second;
135 break;
136 }
137 }
138 }
139
140 std::string resourcePath = LANGUAGE_RESOURCE_PARENT_PATH + languagePath + LANGUAGE_RESOURCE_CHILD_PATH;
141 NETMGR_LOG_I("UpdateResourceMap: resourcePath = %{public}s", resourcePath.c_str());
142 if (!std::filesystem::exists(resourcePath)) {
143 NETMGR_LOG_E("resource path not exist: %{public}s", resourcePath.c_str());
144 return;
145 }
146 /* 从resourcePath中拿到resourceMap */
147 ParseJSONFile(resourcePath, resourceMap);
148 }
149
GetDayNotificationText()150 std::string NetMgrNetStatsLimitNotification::GetDayNotificationText()
151 {
152 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification::GetDayNotificationText");
153 int32_t simId = DelayedSingleton<NetStatsService>::GetInstance()->GetCurActiviteSimId();
154 auto settingsObserverMap_ = DelayedSingleton<NetStatsService>::GetInstance()->GetSettingsObserverMap();
155 if (settingsObserverMap_.find(simId) == settingsObserverMap_.end()) {
156 NETMGR_LOG_I("settingsObserverMap_ has no simId key:: simId %{public}d", simId);
157 return "";
158 }
159
160 std::string outText = resourceMap[KEY_DAILY_NOTIFY_TEXT];
161 NETMGR_LOG_I("NetMgrNetStatsLimitNotification:: simId [%{public}d]", simId);
162 if (outText.find("%s") == std::string::npos) {
163 NETMGR_LOG_I("incorrect format [%{public}s]", outText.c_str());
164 return "";
165 }
166 uint64_t traffic = settingsObserverMap_[simId].second->monthlyLimit;
167 double dailyTraffic = traffic / 100.0 * settingsObserverMap_[simId].second->dailyMark;
168 std::string num = GetTrafficNum(dailyTraffic);
169 outText = outText.replace(outText.find("%s"), TWO_CHAR, num);
170 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification::outText [%{public}s]", outText.c_str());
171 return outText;
172 }
173
GetMonthNotificationText()174 std::string NetMgrNetStatsLimitNotification::GetMonthNotificationText()
175 {
176 std::string outText;
177
178 int32_t simId = DelayedSingleton<NetStatsService>::GetInstance()->GetCurActiviteSimId();
179
180 auto settingsObserverMap_ = DelayedSingleton<NetStatsService>::GetInstance()->GetSettingsObserverMap();
181 if (settingsObserverMap_.find(simId) == settingsObserverMap_.end()) {
182 NETMGR_LOG_I("settingsObserverMap_ has no simId key:: simId %{public}d", simId);
183 return "";
184 }
185
186 int32_t monUsedPercent = settingsObserverMap_[simId].second->monthlyMark;
187
188 outText = resourceMap[KEY_MONTH_NOTIFY_TEXT];
189 if (outText.find("%s") == std::string::npos) {
190 NETMGR_LOG_I("incorrect format [%{public}s]", outText.c_str());
191 return "";
192 }
193 outText = outText.replace(outText.find("%s"), TWO_CHAR, std::to_string(monUsedPercent) + "%");
194 NETMGR_LOG_I("GetMonthNotificationText outText [%{public}s]", outText.c_str());
195 return outText;
196 }
197
GetMonthAlertText()198 std::string NetMgrNetStatsLimitNotification::GetMonthAlertText()
199 {
200 std::string outText;
201
202 int32_t simId = DelayedSingleton<NetStatsService>::GetInstance()->GetCurActiviteSimId();
203
204 auto settingsObserverMap_ = DelayedSingleton<NetStatsService>::GetInstance()->GetSettingsObserverMap();
205 if (settingsObserverMap_.find(simId) == settingsObserverMap_.end()) {
206 NETMGR_LOG_I("settingsObserverMap_ has no simId key:: simId %{public}d", simId);
207 return "";
208 }
209
210 // 检验simId是否合法
211 outText = resourceMap[KEY_MONTH_LIMIT_TEXT];
212 if (outText.find("%s") == std::string::npos) {
213 NETMGR_LOG_I("incorrect format [%{public}s]", outText.c_str());
214 return "";
215 }
216 std::string num = GetTrafficNum(settingsObserverMap_[simId].second->monthlyLimit);
217 outText = outText.replace(outText.find("%s"), TWO_CHAR, num);
218 return outText;
219 }
220
GetNotificationTile(std::string & notificationType)221 std::string NetMgrNetStatsLimitNotification::GetNotificationTile(std::string ¬ificationType)
222 {
223 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification::GetNotificationTile");
224 std::string outText;
225 int32_t simId = DelayedSingleton<NetStatsService>::GetInstance()->GetCurActiviteSimId();
226
227 outText = resourceMap[notificationType];
228
229 if (outText.find("%d") == std::string::npos) {
230 NETMGR_LOG_I("incorrect format %{public}s", outText.c_str());
231 return "";
232 }
233 int32_t slotId = Telephony::CoreServiceClient::GetInstance().GetSlotId(simId);
234 NETMGR_LOG_I("GetNotificationTile. simId:%{public}d, slotId:%{public}d", simId, slotId);
235 outText = outText.replace(outText.find("%d"), TWO_CHAR, std::to_string(slotId + 1));
236 return outText;
237 }
238
SetTitleAndText(int notificationId,std::shared_ptr<Notification::NotificationNormalContent> content,bool isDaulCard)239 bool NetMgrNetStatsLimitNotification::SetTitleAndText(
240 int notificationId,
241 std::shared_ptr<Notification::NotificationNormalContent> content,
242 bool isDaulCard)
243 {
244 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification::SetTitleAndText");
245 std::string strText;
246 if (content == nullptr) {
247 NETMGR_LOG_I("content is null");
248 return false;
249 }
250
251 std::string title = "";
252 if (isDaulCard) {
253 title = (notificationId == NETMGR_STATS_LIMIT_DAY) ? KEY_NETWORK_DAY_MARK_DUAL_TITLE : title;
254 title = (notificationId == NETMGR_STATS_LIMIT_MONTH) ? KEY_NETWORK_MONTH_MARK_DUAL_TITLE : title;
255 title = (notificationId == NETMGR_STATS_ALERT_MONTH) ? KEY_NETWORK_MONTH_LIMIT_DUAL_TITLE : title;
256 } else {
257 title = (notificationId == NETMGR_STATS_LIMIT_DAY) ? KEY_NETWORK_DAY_MARK_SIMGLE_TITLE : title;
258 title = (notificationId == NETMGR_STATS_LIMIT_MONTH) ? KEY_NETWORK_MONTH_MARK_SIMGLE_TITLE : title;
259 title = (notificationId == NETMGR_STATS_ALERT_MONTH) ? KEY_NETWORK_MONTH_LIMIT_SIMGLE_TITLE : title;
260 }
261
262 if (resourceMap.find(title) == resourceMap.end()) {
263 NETMGR_LOG_I("cannot get title from resources");
264 return false;
265 }
266 std::string strTitle;
267 if (isDaulCard) {
268 strTitle = GetNotificationTile(title);
269 } else {
270 strTitle = resourceMap[title];
271 }
272
273 NETMGR_LOG_I("NetMgrNetStatsLimitNotification: strTitle = %{public}s", strTitle.c_str());
274
275 switch (notificationId) {
276 case NETMGR_STATS_LIMIT_DAY:
277 strText = GetDayNotificationText();
278 break;
279 case NETMGR_STATS_LIMIT_MONTH:
280 strText = GetMonthNotificationText();
281 break;
282 case NETMGR_STATS_ALERT_MONTH:
283 strText = GetMonthAlertText();
284 break;
285 default:
286 NETMGR_LOG_I("unknown notification ID");
287 return false;
288 }
289 content->SetText(strText);
290 content->SetTitle(strTitle);
291 NETMGR_LOG_I("end NetMgrNetStatsLimitNotification::SetTitleAndText");
292 return true;
293 }
294
GetPixelMap()295 void NetMgrNetStatsLimitNotification::GetPixelMap()
296 {
297 if (netmgrStatsLimitIconPixelMap_ != nullptr) {
298 return;
299 }
300
301 if (!std::filesystem::exists(NETWORK_ICON_PATH)) {
302 return;
303 }
304
305 uint32_t errorCode = 0;
306 Media::SourceOptions opts;
307 opts.formatHint = "image/png";
308 auto imageSource = Media::ImageSource::CreateImageSource(NETWORK_ICON_PATH, opts, errorCode);
309 if (imageSource == nullptr) {
310 NETMGR_LOG_I("CreateImageSource null");
311 return;
312 }
313 Media::DecodeOptions decodeOpts;
314 std::unique_ptr<Media::PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, errorCode);
315 netmgrStatsLimitIconPixelMap_ = std::move(pixelMap);
316 }
317
GetInstance()318 NetMgrNetStatsLimitNotification& NetMgrNetStatsLimitNotification::GetInstance()
319 {
320 static NetMgrNetStatsLimitNotification instance;
321 return instance;
322 }
323
NetMgrNetStatsLimitNotification()324 NetMgrNetStatsLimitNotification::NetMgrNetStatsLimitNotification()
325 {
326 std::lock_guard<std::mutex> lock(mutex_);
327 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification");
328 ParseJSONFile(LOCALE_TO_RESOURCE_PATH, languageMap);
329 UpdateResourceMap();
330 GetPixelMap();
331 NETMGR_LOG_I("end NetMgrNetStatsLimitNotification");
332 }
333
~NetMgrNetStatsLimitNotification()334 NetMgrNetStatsLimitNotification::~NetMgrNetStatsLimitNotification()
335 {
336 NETMGR_LOG_I("NetMgr Notification destructor enter.");
337 }
338
PublishNetStatsLimitNotification(int notificationId,bool isDaulCard)339 void NetMgrNetStatsLimitNotification::PublishNetStatsLimitNotification(int notificationId, bool isDaulCard)
340 {
341 std::lock_guard<std::mutex> lock(mutex_);
342 NETMGR_LOG_I("PublishNetMgrNetStatsLimitNotification: id = %{public}d", notificationId);
343 UpdateResourceMap();
344 std::shared_ptr<Notification::NotificationNormalContent> notificationContent =
345 std::make_shared<Notification::NotificationNormalContent>();
346 if (notificationContent == nullptr) {
347 NETMGR_LOG_I("get notification content nullptr");
348 return;
349 }
350 if (!SetTitleAndText(notificationId, notificationContent, isDaulCard)) {
351 NETMGR_LOG_I("error setting title and text");
352 return;
353 }
354
355 std::shared_ptr<Notification::NotificationContent> content =
356 std::make_shared<Notification::NotificationContent>(notificationContent);
357 if (content == nullptr) {
358 NETMGR_LOG_I("get notification content nullptr");
359 return;
360 }
361
362 Notification::NotificationRequest request;
363 request.SetNotificationId(static_cast<int32_t>(notificationId));
364 request.SetContent(content);
365
366 request.SetCreatorUid(COMM_NETSYS_NATIVE_SYS_ABILITY_ID);
367 request.SetAutoDeletedTime(NTF_AUTO_DELETE_TIME);
368 request.SetTapDismissed(true);
369 request.SetSlotType(OHOS::Notification::NotificationConstant::SlotType::SOCIAL_COMMUNICATION);
370 request.SetNotificationControlFlags(NETMGR_TRAFFIC_NTF_CONTROL_FLAG);
371
372 request.SetLittleIcon(netmgrStatsLimitIconPixelMap_);
373
374 int ret = Notification::NotificationHelper::PublishNotification(request);
375 NETMGR_LOG_I("publish notification result = %{public}d", ret);
376
377 auto settingsObserverMap_ = DelayedSingleton<NetStatsService>::GetInstance()->GetSettingsObserverMap();
378 }
379
RegNotificationCallback(NetMgrStatsLimitNtfCallback callback)380 void NetMgrNetStatsLimitNotification::RegNotificationCallback(NetMgrStatsLimitNtfCallback callback)
381 {
382 std::lock_guard<std::mutex> lock(g_callbackMutex);
383 g_NetMgrStatsLimitNtfCallback = callback;
384 }
385
GetTrafficNum(double traffic)386 std::string NetMgrNetStatsLimitNotification::GetTrafficNum(double traffic)
387 {
388 const char* units[] = {"B", "KB", "MB", "GB", "TB"}; // 流量单位数组
389 int record = 0; // 记录单位索引
390
391 // 确定应该使用的单位
392 while (traffic >= UNIT_CONVERT_1024 && record < 4) { // 4: 防止units数组越界
393 traffic /= UNIT_CONVERT_1024;
394 record++;
395 }
396
397 std::ostringstream oss;
398 oss << std::fixed << std::setprecision(2) << traffic << " " << units[record]; // 2: 保留两位小数
399 // 返回格式化后的字符串
400 return oss.str();
401 }
402 } // namespace NetManagerStandard
403 } // namespace OHOS
404
405