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_SINGLE_TITLE = "netstats_excess_monthlimit_notofication_title";
62 static constexpr const char *KEY_NETWORK_MONTH_MARK_SINGLE_TITLE = "netstats_excess_monthmark_notofication_title";
63 static constexpr const char *KEY_NETWORK_DAY_MARK_SINGLE_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 char tmpPath[PATH_MAX] = { 0 };
142 if (!realpath(resourcePath.c_str(), tmpPath)) {
143 NETMGR_LOG_E("file name is illegal");
144 return;
145 }
146 resourcePath = tmpPath;
147 NETMGR_LOG_I("UpdateResourceMap: resourcePath = %{public}s", resourcePath.c_str());
148 if (!std::filesystem::exists(resourcePath)) {
149 NETMGR_LOG_E("resource path not exist: %{public}s", resourcePath.c_str());
150 return;
151 }
152 /* 从resourcePath中拿到resourceMap */
153 ParseJSONFile(resourcePath, resourceMap);
154 }
155
GetDayNotificationText()156 std::string NetMgrNetStatsLimitNotification::GetDayNotificationText()
157 {
158 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification::GetDayNotificationText, simId:%{public}d", simId_);
159 if (resourceMap.find(KEY_DAILY_NOTIFY_TEXT) == resourceMap.end() ||
160 resourceMap[KEY_DAILY_NOTIFY_TEXT].find("%s") == std::string::npos) {
161 NETMGR_LOG_E("GetDayNotificationText error");
162 return "";
163 }
164
165 std::string outText = resourceMap[KEY_DAILY_NOTIFY_TEXT];
166 int32_t simId = simId_;
167 uint64_t traffic = 0;
168 uint16_t dayPercent = 0;
169 bool ret = DelayedSingleton<NetStatsService>::GetInstance()->GetdailyMarkBySimId(simId, dayPercent);
170 DelayedSingleton<NetStatsService>::GetInstance()->GetMonthlyLimitBySimId(simId, traffic);
171 if (!ret) {
172 NETMGR_LOG_E("simId does not exist:: simId %{public}d", simId);
173 return "";
174 }
175
176 double dailyTraffic = static_cast<double>(traffic) / 100 * dayPercent;
177 std::string num = GetTrafficNum(dailyTraffic);
178 outText = outText.replace(outText.find("%s"), TWO_CHAR, num);
179 NETMGR_LOG_I("NetMgrNetStatsLimitNotification::outText [%{public}s]", outText.c_str());
180 return outText;
181 }
182
GetMonthNotificationText()183 std::string NetMgrNetStatsLimitNotification::GetMonthNotificationText()
184 {
185 if (resourceMap.find(KEY_MONTH_NOTIFY_TEXT) == resourceMap.end() ||
186 resourceMap[KEY_MONTH_NOTIFY_TEXT].find("%s") == std::string::npos) {
187 NETMGR_LOG_E("GetMonthNotificationText error");
188 return "";
189 }
190 std::string outText = resourceMap[KEY_MONTH_NOTIFY_TEXT];
191
192 int32_t simId = simId_;
193 uint16_t monUsedPercent = 0;
194 bool ret = DelayedSingleton<NetStatsService>::GetInstance()->GetMonthlyMarkBySimId(simId, monUsedPercent);
195 if (!ret) {
196 NETMGR_LOG_E("simId does not exist:: simId %{public}d", simId);
197 return "";
198 }
199 std::string percent = "" + std::to_string(monUsedPercent) + "%" + "";
200 outText = outText.replace(outText.find("%s"), TWO_CHAR, percent);
201 NETMGR_LOG_I("GetMonthNotificationText outText [%{public}s]", outText.c_str());
202 return outText;
203 }
204
GetMonthAlertText()205 std::string NetMgrNetStatsLimitNotification::GetMonthAlertText()
206 {
207 if (resourceMap.find(KEY_MONTH_LIMIT_TEXT) == resourceMap.end() ||
208 resourceMap[KEY_MONTH_LIMIT_TEXT].find("%s") == std::string::npos) {
209 NETMGR_LOG_E("GetMonthAlertText error");
210 return "";
211 }
212
213 std::string outText = resourceMap[KEY_MONTH_LIMIT_TEXT];
214 int32_t simId = simId_;
215 uint64_t traffic = 0;
216 bool ret = DelayedSingleton<NetStatsService>::GetInstance()->GetMonthlyLimitBySimId(simId, traffic);
217 NETMGR_LOG_I("GetMonthAlertText trafficLimit:%{public}" PRIu64 "", traffic);
218 if (!ret) {
219 return "";
220 }
221 std::string num = GetTrafficNum(static_cast<double>(traffic));
222 outText = outText.replace(outText.find("%s"), TWO_CHAR, num);
223 NETMGR_LOG_I("GetMonthAlertText::outText [%{public}s]", outText.c_str());
224 return outText;
225 }
226
GetNotificationTitle(std::string & notificationType)227 std::string NetMgrNetStatsLimitNotification::GetNotificationTitle(std::string ¬ificationType)
228 {
229 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification::GetNotificationTitle");
230 std::string outText = resourceMap[notificationType];
231 if (outText.find("%d") == std::string::npos) {
232 NETMGR_LOG_I("incorrect format %{public}s", outText.c_str());
233 return "";
234 }
235
236 int32_t simId = simId_;
237 int32_t slotId = Telephony::CoreServiceClient::GetInstance().GetSlotId(simId);
238 NETMGR_LOG_I("GetNotificationTitle. simId:%{public}d, slotId:%{public}d", simId, slotId);
239 outText = outText.replace(outText.find("%d"), TWO_CHAR, std::to_string(slotId + 1));
240 return outText;
241 }
242
SetTitleAndText(int notificationId,std::shared_ptr<Notification::NotificationNormalContent> content,bool isDualCard)243 bool NetMgrNetStatsLimitNotification::SetTitleAndText(
244 int notificationId,
245 std::shared_ptr<Notification::NotificationNormalContent> content,
246 bool isDualCard)
247 {
248 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification::SetTitleAndText");
249 if (content == nullptr) {
250 NETMGR_LOG_E("content is null");
251 return false;
252 }
253
254 std::string title = "";
255 if (isDualCard) {
256 title = (notificationId == NETMGR_STATS_LIMIT_DAY) ? KEY_NETWORK_DAY_MARK_DUAL_TITLE : title;
257 title = (notificationId == NETMGR_STATS_LIMIT_MONTH) ? KEY_NETWORK_MONTH_MARK_DUAL_TITLE : title;
258 title = (notificationId == NETMGR_STATS_ALERT_MONTH) ? KEY_NETWORK_MONTH_LIMIT_DUAL_TITLE : title;
259 } else {
260 title = (notificationId == NETMGR_STATS_LIMIT_DAY) ? KEY_NETWORK_DAY_MARK_SINGLE_TITLE : title;
261 title = (notificationId == NETMGR_STATS_LIMIT_MONTH) ? KEY_NETWORK_MONTH_MARK_SINGLE_TITLE : title;
262 title = (notificationId == NETMGR_STATS_ALERT_MONTH) ? KEY_NETWORK_MONTH_LIMIT_SINGLE_TITLE : title;
263 }
264
265 if (resourceMap.find(title) == resourceMap.end()) {
266 NETMGR_LOG_E("cannot get title from resources");
267 return false;
268 }
269 std::string strTitle;
270 if (isDualCard) {
271 strTitle = GetNotificationTitle(title);
272 } else {
273 strTitle = resourceMap[title];
274 }
275 NETMGR_LOG_I("NetMgrNetStatsLimitNotification: strTitle = %{public}s", strTitle.c_str());
276
277 std::string strText;
278 switch (notificationId) {
279 case NETMGR_STATS_LIMIT_DAY:
280 strText = GetDayNotificationText();
281 break;
282 case NETMGR_STATS_LIMIT_MONTH:
283 strText = GetMonthNotificationText();
284 break;
285 case NETMGR_STATS_ALERT_MONTH:
286 strText = GetMonthAlertText();
287 break;
288 default:
289 NETMGR_LOG_I("unknown notification ID");
290 return false;
291 }
292 content->SetText(strText);
293 content->SetTitle(strTitle);
294 NETMGR_LOG_I("end NetMgrNetStatsLimitNotification::SetTitleAndText");
295 return true;
296 }
297
GetPixelMap()298 void NetMgrNetStatsLimitNotification::GetPixelMap()
299 {
300 if (netmgrStatsLimitIconPixelMap_ != nullptr) {
301 return;
302 }
303
304 if (!std::filesystem::exists(NETWORK_ICON_PATH)) {
305 return;
306 }
307
308 uint32_t errorCode = 0;
309 Media::SourceOptions opts;
310 opts.formatHint = "image/png";
311 auto imageSource = Media::ImageSource::CreateImageSource(NETWORK_ICON_PATH, opts, errorCode);
312 if (imageSource == nullptr) {
313 NETMGR_LOG_I("CreateImageSource null");
314 return;
315 }
316 Media::DecodeOptions decodeOpts;
317 std::unique_ptr<Media::PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, errorCode);
318 netmgrStatsLimitIconPixelMap_ = std::move(pixelMap);
319 }
320
GetInstance()321 NetMgrNetStatsLimitNotification& NetMgrNetStatsLimitNotification::GetInstance()
322 {
323 static NetMgrNetStatsLimitNotification instance;
324 return instance;
325 }
326
NetMgrNetStatsLimitNotification()327 NetMgrNetStatsLimitNotification::NetMgrNetStatsLimitNotification()
328 {
329 std::lock_guard<std::mutex> lock(mutex_);
330 NETMGR_LOG_I("start NetMgrNetStatsLimitNotification");
331 ParseJSONFile(LOCALE_TO_RESOURCE_PATH, languageMap);
332 UpdateResourceMap();
333 GetPixelMap();
334 NETMGR_LOG_I("end NetMgrNetStatsLimitNotification");
335 }
336
~NetMgrNetStatsLimitNotification()337 NetMgrNetStatsLimitNotification::~NetMgrNetStatsLimitNotification()
338 {
339 NETMGR_LOG_I("NetMgr Notification destructor enter.");
340 }
341
PublishNetStatsLimitNotification(int notificationId,int simId,bool isDualCard)342 void NetMgrNetStatsLimitNotification::PublishNetStatsLimitNotification(int notificationId, int simId, bool isDualCard)
343 {
344 std::lock_guard<std::mutex> lock(mutex_);
345 simId_ = simId;
346 NETMGR_LOG_I("PublishNetMgrNetStatsLimitNotification: id = %{public}d", notificationId);
347 UpdateResourceMap();
348 std::shared_ptr<Notification::NotificationNormalContent> notificationContent =
349 std::make_shared<Notification::NotificationNormalContent>();
350 if (notificationContent == nullptr) {
351 NETMGR_LOG_E("get notification content nullptr");
352 return;
353 }
354 if (!SetTitleAndText(notificationId, notificationContent, isDualCard)) {
355 NETMGR_LOG_E("error setting title and text");
356 return;
357 }
358
359 std::shared_ptr<Notification::NotificationContent> content =
360 std::make_shared<Notification::NotificationContent>(notificationContent);
361 if (content == nullptr) {
362 NETMGR_LOG_E("get notification content nullptr");
363 return;
364 }
365
366 Notification::NotificationRequest request;
367 request.SetNotificationId(static_cast<int32_t>(notificationId));
368 request.SetContent(content);
369 request.SetCreatorUid(COMM_NETSYS_NATIVE_SYS_ABILITY_ID);
370 request.SetAutoDeletedTime(NTF_AUTO_DELETE_TIME);
371 request.SetTapDismissed(true);
372 request.SetSlotType(OHOS::Notification::NotificationConstant::SlotType::SOCIAL_COMMUNICATION);
373 request.SetNotificationControlFlags(NETMGR_TRAFFIC_NTF_CONTROL_FLAG);
374 request.SetLittleIcon(netmgrStatsLimitIconPixelMap_);
375 int ret = Notification::NotificationHelper::PublishNotification(request);
376 NETMGR_LOG_I("publish notification result = %{public}d", ret);
377 }
378
RegNotificationCallback(NetMgrStatsLimitNtfCallback callback)379 void NetMgrNetStatsLimitNotification::RegNotificationCallback(NetMgrStatsLimitNtfCallback callback)
380 {
381 std::lock_guard<std::mutex> lock(g_callbackMutex);
382 g_NetMgrStatsLimitNtfCallback = callback;
383 }
384
GetTrafficNum(double traffic)385 std::string NetMgrNetStatsLimitNotification::GetTrafficNum(double traffic)
386 {
387 const char* units[] = {"B", "KB", "MB", "GB", "TB"};
388 int record = 0;
389 while (traffic >= UNIT_CONVERT_1024 && record < 4) { // 4: units array max index
390 traffic /= UNIT_CONVERT_1024;
391 record++;
392 }
393
394 std::ostringstream oss;
395 oss << std::fixed << std::setprecision(2) << traffic << " " << units[record]; // 2: 保留两位小数
396 std::string systemLocalStr = Global::I18n::LocaleConfig::GetSystemLocale();
397 auto ret = Global::I18n::LocaleConfig::IsRTL(systemLocalStr);
398 if (ret) {
399 return "" + oss.str() + "";
400 }
401 return oss.str();
402 }
403 } // namespace NetManagerStandard
404 } // namespace OHOS
405