1 /*
2  * Copyright (c) 2024-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 #ifdef NOTIFICATION_SMART_REMINDER_SUPPORTED
17 #include "smart_reminder_center.h"
18 
19 #include "ans_log_wrapper.h"
20 #include "ipc_skeleton.h"
21 #include "notification_bundle_option.h"
22 #include "notification_local_live_view_content.h"
23 #include "notification_preferences.h"
24 #include "os_account_manager.h"
25 #include "screenlock_manager.h"
26 #include "string_utils.h"
27 
28 namespace OHOS {
29 namespace Notification {
30 using namespace std;
SmartReminderCenter()31 SmartReminderCenter::SmartReminderCenter()
32 {
33     if (!DelayedSingleton<NotificationConfigParse>::GetInstance()->GetCurrentSlotReminder(currentReminderMethods_)) {
34         return;
35     }
36     GetMultiDeviceReminder();
37 }
38 
GetMultiDeviceReminder()39 void SmartReminderCenter::GetMultiDeviceReminder()
40 {
41     nlohmann::json root;
42     string multiJsonPoint = "/";
43     multiJsonPoint.append(NotificationConfigParse::CFG_KEY_NOTIFICATION_SERVICE);
44     multiJsonPoint.append("/");
45     multiJsonPoint.append(MULTI_DEVICE_REMINDER);
46     if (!DelayedSingleton<NotificationConfigParse>::GetInstance()->GetConfigJson(multiJsonPoint, root)) {
47         ANS_LOGW("Failed to get multiDeviceReminder CCM config file.");
48         return;
49     }
50 
51     if (root.find(NotificationConfigParse::CFG_KEY_NOTIFICATION_SERVICE) == root.end()) {
52         ANS_LOGW("GetMultiDeviceReminder failed as can not find notificationService.");
53         return;
54     }
55 
56     nlohmann::json multiDeviceRemindJson =
57         root[NotificationConfigParse::CFG_KEY_NOTIFICATION_SERVICE][MULTI_DEVICE_REMINDER];
58     if (multiDeviceRemindJson.is_null() || !multiDeviceRemindJson.is_array() || multiDeviceRemindJson.empty()) {
59         ANS_LOGW("GetMultiDeviceReminder failed as invalid multiDeviceReminder json.");
60         return;
61     }
62 
63     reminderMethods_.clear();
64     for (auto &singleDeviceRemindJson : multiDeviceRemindJson) {
65         if (singleDeviceRemindJson.is_null() || !singleDeviceRemindJson.is_object()) {
66             continue;
67         }
68 
69         if (singleDeviceRemindJson.find(ReminderAffected::DEVICE_TYPE) == singleDeviceRemindJson.end() ||
70             singleDeviceRemindJson[ReminderAffected::DEVICE_TYPE].is_null() ||
71             !singleDeviceRemindJson[ReminderAffected::DEVICE_TYPE].is_string()) {
72             continue;
73         }
74 
75         if (singleDeviceRemindJson.find(REMINDER_FILTER_DEVICE) == singleDeviceRemindJson.end() ||
76             singleDeviceRemindJson[REMINDER_FILTER_DEVICE].is_null() ||
77             !singleDeviceRemindJson[REMINDER_FILTER_DEVICE].is_array() ||
78             singleDeviceRemindJson[REMINDER_FILTER_DEVICE].empty()) {
79             continue;
80         }
81         ParseReminderFilterDevice(singleDeviceRemindJson[REMINDER_FILTER_DEVICE],
82             singleDeviceRemindJson[ReminderAffected::DEVICE_TYPE].get<string>());
83     }
84 
85     if (reminderMethods_.size() <= 0) {
86         ANS_LOGW("GetMultiDeviceReminder failed as Invalid reminderMethods size.");
87     }
88 }
89 
ParseReminderFilterDevice(const nlohmann::json & root,const string & deviceType)90 void SmartReminderCenter::ParseReminderFilterDevice(const nlohmann::json &root, const string &deviceType)
91 {
92     map<string, vector<shared_ptr<ReminderAffected>>> reminderFilterDevice;
93     for (auto &reminderFilterDeviceJson : root) {
94         NotificationConstant::SlotType slotType;
95         if (reminderFilterDeviceJson.find(SLOT_TYPE) == reminderFilterDeviceJson.end() ||
96             reminderFilterDeviceJson[SLOT_TYPE].is_null() ||
97             !reminderFilterDeviceJson[SLOT_TYPE].is_string()) {
98             continue;
99         }
100 
101         if (reminderFilterDeviceJson.find(REMINDER_FILTER_SLOT) == reminderFilterDeviceJson.end() ||
102             reminderFilterDeviceJson[REMINDER_FILTER_SLOT].is_null() ||
103             !reminderFilterDeviceJson[REMINDER_FILTER_SLOT].is_array() ||
104             reminderFilterDeviceJson[REMINDER_FILTER_SLOT].empty()) {
105             continue;
106         }
107 
108         std::string slotTypes = reminderFilterDeviceJson[SLOT_TYPE].get<std::string>();
109         std::vector<std::string> slotTypeVector;
110         StringUtils::Split(slotTypes, SPLIT_FLAG, slotTypeVector);
111 
112         for (std::string slotTypeStr : slotTypeVector) {
113             if (!NotificationSlot::GetSlotTypeByString(slotTypeStr, slotType)) {
114                 continue;
115             }
116             ParseReminderFilterSlot(reminderFilterDeviceJson[REMINDER_FILTER_SLOT],
117                 to_string(static_cast<int32_t>(slotType)), reminderFilterDevice);
118         }
119     }
120     if (reminderFilterDevice.size() > 0) {
121         reminderMethods_[deviceType] = move(reminderFilterDevice);
122     } else {
123         ANS_LOGI("ParseReminderFilterDevice failed as Invalid reminderFilterDevice size. deviceType = %{public}s.",
124             deviceType.c_str());
125     }
126 }
127 
ParseReminderFilterSlot(const nlohmann::json & root,const string & notificationType,map<string,vector<shared_ptr<ReminderAffected>>> & reminderFilterDevice) const128 void SmartReminderCenter::ParseReminderFilterSlot(
129     const nlohmann::json &root,
130     const string &notificationType,
131     map<string, vector<shared_ptr<ReminderAffected>>> &reminderFilterDevice) const
132 {
133     vector<shared_ptr<ReminderAffected>> reminderFilterSlot;
134     for (auto &reminderFilterSlotJson : root) {
135         NotificationContent::Type contentType;
136         bool validContentType = true;
137 
138         if (reminderFilterSlotJson.find(CONTENT_TYPE) == reminderFilterSlotJson.end() ||
139             reminderFilterSlotJson[CONTENT_TYPE].is_null() ||
140             !reminderFilterSlotJson[CONTENT_TYPE].is_string() ||
141             !NotificationContent::GetContentTypeByString(
142                 reminderFilterSlotJson[CONTENT_TYPE].get<std::string>(), contentType)) {
143             validContentType = false;
144         }
145 
146         if (reminderFilterSlotJson.find(REMINDER_FILTER_CONTENT) == reminderFilterSlotJson.end() ||
147             reminderFilterSlotJson[REMINDER_FILTER_CONTENT].is_null() ||
148             !reminderFilterSlotJson[REMINDER_FILTER_CONTENT].is_array() ||
149             reminderFilterSlotJson[REMINDER_FILTER_CONTENT].empty()) {
150             validContentType = false;
151         }
152 
153         if (validContentType) {
154             string localNotificationType = notificationType;
155             localNotificationType.append("#");
156             localNotificationType.append(to_string(static_cast<int32_t>(contentType)));
157             ParseReminderFilterContent(
158                 reminderFilterSlotJson[REMINDER_FILTER_CONTENT], localNotificationType, reminderFilterDevice);
159             continue;
160         }
161         shared_ptr<ReminderAffected> reminderAffected = make_shared<ReminderAffected>();
162         if (reminderAffected->FromJson(reminderFilterSlotJson)) {
163             reminderFilterSlot.push_back(reminderAffected);
164         }
165     }
166     if (reminderFilterSlot.size() > 0) {
167         reminderFilterDevice[notificationType] = move(reminderFilterSlot);
168     }
169 }
170 
ParseReminderFilterContent(const nlohmann::json & root,const string & notificationType,map<string,vector<shared_ptr<ReminderAffected>>> & reminderFilterDevice) const171 void SmartReminderCenter::ParseReminderFilterContent(
172     const nlohmann::json &root,
173     const string &notificationType,
174     map<string, vector<shared_ptr<ReminderAffected>>> &reminderFilterDevice) const
175 {
176     vector<shared_ptr<ReminderAffected>> reminderFilterContent;
177     for (auto &reminderFilterContentJson : root) {
178         bool validTypeCode = true;
179         if (reminderFilterContentJson.find(TYPE_CODE) == reminderFilterContentJson.end() ||
180             reminderFilterContentJson[TYPE_CODE].is_null() ||
181             !reminderFilterContentJson[TYPE_CODE].is_number()) {
182             validTypeCode = false;
183         }
184 
185         if (reminderFilterContentJson.find(REMINDER_FILTER_CODE) == reminderFilterContentJson.end() ||
186             reminderFilterContentJson[REMINDER_FILTER_CODE].is_null() ||
187             !reminderFilterContentJson[REMINDER_FILTER_CODE].is_array() ||
188             reminderFilterContentJson[REMINDER_FILTER_CODE].empty()) {
189             validTypeCode = false;
190         }
191 
192         if (validTypeCode) {
193             int32_t typeCode = reminderFilterContentJson[TYPE_CODE].get<int32_t>();
194             string localNotificationType = notificationType;
195             localNotificationType.append("#");
196             localNotificationType.append(to_string(typeCode));
197             ParseReminderFilterCode(
198                 reminderFilterContentJson[REMINDER_FILTER_CODE], localNotificationType, reminderFilterDevice);
199             continue;
200         }
201         shared_ptr<ReminderAffected> reminderAffected = make_shared<ReminderAffected>();
202         if (reminderAffected->FromJson(reminderFilterContentJson)) {
203             reminderFilterContent.push_back(reminderAffected);
204         }
205     }
206     if (reminderFilterContent.size() > 0) {
207         reminderFilterDevice[notificationType] = move(reminderFilterContent);
208     }
209 }
210 
ParseReminderFilterCode(const nlohmann::json & root,const string & notificationType,map<string,vector<shared_ptr<ReminderAffected>>> & reminderFilterDevice) const211 void SmartReminderCenter::ParseReminderFilterCode(
212     const nlohmann::json &root,
213     const string &notificationType,
214     map<string, vector<shared_ptr<ReminderAffected>>> &reminderFilterDevice) const
215 {
216     vector<shared_ptr<ReminderAffected>> reminderFilterCode;
217     for (auto &reminderFilterCodeJson : root) {
218         shared_ptr<ReminderAffected> reminderAffected = make_shared<ReminderAffected>();
219         if (reminderAffected->FromJson(reminderFilterCodeJson)) {
220             reminderFilterCode.push_back(reminderAffected);
221         }
222     }
223     if (reminderFilterCode.size() > 0) {
224         reminderFilterDevice[notificationType] = move(reminderFilterCode);
225     }
226 }
227 
ReminderDecisionProcess(const sptr<NotificationRequest> & request) const228 void SmartReminderCenter::ReminderDecisionProcess(const sptr<NotificationRequest> &request) const
229 {
230     shared_ptr<map<string, shared_ptr<NotificationFlags>>> notificationFlagsOfDevices =
231         make_shared<map<string, shared_ptr<NotificationFlags>>>();
232     NotificationConstant::SlotType slotType = request->GetSlotType();
233     auto iter = currentReminderMethods_.find(slotType);
234     if (iter != currentReminderMethods_.end()) {
235         // Only config file can set reminder open now. Otherwise, change iter->second to 11111
236         (*notificationFlagsOfDevices)[NotificationConstant::CURRENT_DEVICE_TYPE] = iter->second;
237     }
238 
239     map<string, vector<shared_ptr<ReminderAffected>>> currentReminderMethod;
240     set<string> validDevices;
241     for (auto &reminderMethod : reminderMethods_) {
242         if (reminderMethod.first.compare(NotificationConstant::CURRENT_DEVICE_TYPE) == 0) {
243             currentReminderMethod = reminderMethod.second;
244             continue;
245         }
246         HandleReminderMethods(
247             reminderMethod.first, reminderMethod.second, request, validDevices, notificationFlagsOfDevices);
248     }
249     if (currentReminderMethod.size() > 0 && validDevices.size() > 0) {
250         HandleReminderMethods(NotificationConstant::CURRENT_DEVICE_TYPE,
251             currentReminderMethod, request, validDevices, notificationFlagsOfDevices);
252     }
253 
254     request->SetDeviceFlags(notificationFlagsOfDevices);
255 }
256 
HandleReminderMethods(const string & deviceType,const map<string,vector<shared_ptr<ReminderAffected>>> & reminderFilterDevice,const sptr<NotificationRequest> & request,set<string> & validDevices,shared_ptr<map<string,shared_ptr<NotificationFlags>>> notificationFlagsOfDevices) const257 void SmartReminderCenter::HandleReminderMethods(
258     const string &deviceType,
259     const map<string, vector<shared_ptr<ReminderAffected>>> &reminderFilterDevice,
260     const sptr<NotificationRequest> &request,
261     set<string> &validDevices,
262     shared_ptr<map<string, shared_ptr<NotificationFlags>>> notificationFlagsOfDevices) const
263 {
264     vector<shared_ptr<ReminderAffected>> reminderAffecteds;
265     GetReminderAffecteds(reminderFilterDevice, request, reminderAffecteds);
266     if (reminderAffecteds.size() <= 0) {
267         return;
268     }
269     bitset<DistributedDeviceStatus::STATUS_SIZE> bitStatus;
270     GetDeviceStatusByType(deviceType, bitStatus);
271     bool enabledAffectedBy = true;
272     if (deviceType.compare(NotificationConstant::CURRENT_DEVICE_TYPE) != 0) {
273         if (IsNeedSynergy(deviceType, request->GetOwnerBundleName(), request->GetOwnerUid())) {
274             validDevices.insert(deviceType);
275         } else {
276             enabledAffectedBy = false;
277         }
278     }
279     if (!NotificationSubscriberManager::GetInstance()->GetIsEnableEffectedRemind()) {
280         enabledAffectedBy = false;
281     }
282 
283     for (auto &reminderAffected : reminderAffecteds) {
284         if (!CompareStatus(reminderAffected->status_, bitStatus)) {
285             continue;
286         }
287         if (reminderAffected->affectedBy_.size() <= 0) {
288             (*notificationFlagsOfDevices)[deviceType] = reminderAffected->reminderFlags_;
289             continue;
290         }
291         if (enabledAffectedBy &&
292             HandleAffectedReminder(deviceType, reminderAffected, validDevices, notificationFlagsOfDevices)) {
293             break;
294         }
295     }
296 }
297 
IsNeedSynergy(const string & deviceType,const string & ownerBundleName,int32_t ownerUid) const298 bool SmartReminderCenter::IsNeedSynergy(const string &deviceType, const string &ownerBundleName, int32_t ownerUid) const
299 {
300     bool isEnable = true;
301     if (NotificationPreferences::GetInstance()->IsSmartReminderEnabled(deviceType, isEnable) != ERR_OK || !isEnable) {
302         return false;
303     }
304 
305     sptr<NotificationBundleOption> bundleOption =
306         new (std::nothrow) NotificationBundleOption(ownerBundleName, ownerUid);
307     if (NotificationPreferences::GetInstance()->IsDistributedEnabledByBundle(
308         bundleOption, deviceType, isEnable) != ERR_OK || !isEnable) {
309         return false;
310     }
311     return true;
312 }
313 
HandleAffectedReminder(const string & deviceType,const shared_ptr<ReminderAffected> & reminderAffected,const set<string> & validDevices,shared_ptr<map<string,shared_ptr<NotificationFlags>>> notificationFlagsOfDevices) const314 bool SmartReminderCenter::HandleAffectedReminder(
315     const string &deviceType,
316     const shared_ptr<ReminderAffected> &reminderAffected,
317     const set<string> &validDevices,
318     shared_ptr<map<string, shared_ptr<NotificationFlags>>> notificationFlagsOfDevices) const
319 {
320     bool ret = true;
321     for (auto &affectedBy : reminderAffected->affectedBy_) {
322         if (deviceType.compare(NotificationConstant::CURRENT_DEVICE_TYPE) == 0 &&
323             validDevices.find(affectedBy.first) == validDevices.end()) {
324             ret = false;
325             break;
326         }
327         bitset<DistributedDeviceStatus::STATUS_SIZE> bitStatus;
328         GetDeviceStatusByType(affectedBy.first, bitStatus);
329         if (!CompareStatus(affectedBy.second, bitStatus)) {
330             ret = false;
331             break;
332         }
333     }
334     if (ret) {
335         (*notificationFlagsOfDevices)[deviceType] = reminderAffected->reminderFlags_;
336     }
337     return ret;
338 }
339 
CompareStatus(const string & status,const bitset<DistributedDeviceStatus::STATUS_SIZE> & bitStatus) const340 bool SmartReminderCenter::CompareStatus(
341     const string &status, const bitset<DistributedDeviceStatus::STATUS_SIZE> &bitStatus) const
342 {
343     if (status.size() <= 0) {
344         return true;
345     }
346     // bitset.to_string() and config is reverse, bit[0] is behind
347     string localStatus = status;
348     reverse(localStatus.begin(), localStatus.end());
349     for (int32_t seq = 0; seq < DistributedDeviceStatus::STATUS_SIZE; seq++) {
350         if (localStatus[seq] != ReminderAffected::STATUS_DEFAULT && bitStatus[seq] != localStatus[seq] - '0') {
351             return false;
352         }
353     }
354     return true;
355 }
356 
GetReminderAffecteds(const map<string,vector<shared_ptr<ReminderAffected>>> & reminderFilterDevice,const sptr<NotificationRequest> & request,vector<shared_ptr<ReminderAffected>> & reminderAffecteds) const357 __attribute__((no_sanitize("cfi"))) void SmartReminderCenter::GetReminderAffecteds(
358     const map<string, vector<shared_ptr<ReminderAffected>>> &reminderFilterDevice,
359     const sptr<NotificationRequest> &request,
360     vector<shared_ptr<ReminderAffected>> &reminderAffecteds) const
361 {
362     string strSlotType = to_string(static_cast<int32_t>(request->GetSlotType()));
363     string contentTypeCombination = strSlotType;
364     contentTypeCombination.append("#");
365     if (request->GetContent() != nullptr) {
366         contentTypeCombination.append(to_string(static_cast<int32_t>(request->GetContent()->GetContentType())));
367     }
368     string typeCodeCombination = contentTypeCombination;
369     typeCodeCombination.append("#");
370     if (request->GetContent() != nullptr && request->GetContent()->GetNotificationContent() != nullptr) {
371         NotificationLocalLiveViewContent *localLiveView =
372             static_cast<NotificationLocalLiveViewContent *>(&(*(request->GetContent()->GetNotificationContent())));
373         typeCodeCombination.append(to_string(localLiveView->GetType()));
374     }
375     auto iter = reminderFilterDevice.find(typeCodeCombination);
376     if (iter != reminderFilterDevice.end()) {
377         reminderAffecteds = iter->second;
378         return;
379     }
380     iter = reminderFilterDevice.find(contentTypeCombination);
381     if (iter != reminderFilterDevice.end()) {
382         reminderAffecteds = iter->second;
383         return;
384     }
385     iter = reminderFilterDevice.find(strSlotType);
386     if (iter != reminderFilterDevice.end()) {
387         reminderAffecteds = iter->second;
388         return;
389     }
390     ANS_LOGD("GetReminderAffecteds fail as wrong notification_config.json possibly. TypeCombination = %{public}s.",
391         typeCodeCombination.c_str());
392 }
393 
GetDeviceStatusByType(const string & deviceType,bitset<DistributedDeviceStatus::STATUS_SIZE> & bitStatus) const394 void SmartReminderCenter::GetDeviceStatusByType(
395     const string &deviceType, bitset<DistributedDeviceStatus::STATUS_SIZE> &bitStatus) const
396 {
397     u_int32_t status = DelayedSingleton<DistributedDeviceStatus>::GetInstance()->GetDeviceStatus(deviceType);
398     bitStatus = bitset<DistributedDeviceStatus::STATUS_SIZE>(status);
399     if (deviceType.compare(NotificationConstant::CURRENT_DEVICE_TYPE) == 0) {
400         bool screenLocked = true;
401         screenLocked = ScreenLock::ScreenLockManager::GetInstance()->IsScreenLocked();
402         bitStatus.set(DistributedDeviceStatus::LOCK_FLAG, !screenLocked);
403     }
404     ANS_LOGD("GetDeviceStatusByType deviceType: %{public}s, bitStatus: %{public}s.",
405         deviceType.c_str(), bitStatus.to_string().c_str());
406 }
407 }  // namespace Notification
408 }  // namespace OHOS
409 #endif
410