1 /*
2  * Copyright (c) 2023 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 "notification_dialog_manager.h"
17 
18 #include "common_event_manager.h"
19 #include "matching_skills.h"
20 
21 #include "advanced_notification_service.h"
22 #include "ans_const_define.h"
23 #include "ans_log_wrapper.h"
24 #include "notification_bundle_option.h"
25 #include "notification_dialog.h"
26 #include "notification_preferences.h"
27 #include <cstdlib>
28 #include <string>
29 
30 namespace OHOS::Notification {
31 using DialogInfo = NotificationDialogManager::DialogInfo;
32 
Create(NotificationDialogManager & dialogManager)33 std::shared_ptr<NotificationDialogEventSubscriber> NotificationDialogEventSubscriber::Create(
34     NotificationDialogManager& dialogManager)
35 {
36     ANS_LOGD("enter");
37     EventFwk::MatchingSkills matchingSkills;
38     matchingSkills.AddEvent(NotificationDialogEventSubscriber::EVENT_NAME);
39     EventFwk::CommonEventSubscribeInfo subscriberInfo(matchingSkills);
40     subscriberInfo.SetPublisherBundleName(NotificationDialogManager::NOTIFICATION_DIALOG_SERVICE_BUNDLE);
41     return std::make_shared<NotificationDialogEventSubscriber>(dialogManager, subscriberInfo);
42 }
43 
NotificationDialogEventSubscriber(NotificationDialogManager & dialogManager,const EventFwk::CommonEventSubscribeInfo & subscribeInfo)44 NotificationDialogEventSubscriber::NotificationDialogEventSubscriber(
45     NotificationDialogManager& dialogManager, const EventFwk::CommonEventSubscribeInfo& subscribeInfo)
46     : EventFwk::CommonEventSubscriber(subscribeInfo), dialogManager_(dialogManager)
47 { }
48 
OnReceiveEvent(const EventFwk::CommonEventData & data)49 void NotificationDialogEventSubscriber::OnReceiveEvent(const EventFwk::CommonEventData& data)
50 {
51     int32_t code = data.GetCode();
52     std::string bundleName = data.GetWant().GetStringParam("bundleName");
53     int32_t bundleUid = std::atoi(data.GetWant().GetStringParam("bundleUid").c_str());
54     ANS_LOGI("NotificationDialogEventSubscriber Get Data %{public}d %{public}s %{public}d", code,
55         bundleName.c_str(), bundleUid);
56     dialogManager_.OnBundleEnabledStatusChanged(static_cast<DialogStatus>(code), bundleName, bundleUid);
57 }
58 
~NotificationDialogEventSubscriber()59 NotificationDialogEventSubscriber::~NotificationDialogEventSubscriber()
60 {
61     ANS_LOGD("enter");
62 }
63 
NotificationDialogManager(AdvancedNotificationService & ans)64 NotificationDialogManager::NotificationDialogManager(AdvancedNotificationService& ans)
65     : ans_(ans)
66 {
67     ANS_LOGD("enter");
68 }
69 
~NotificationDialogManager()70 NotificationDialogManager::~NotificationDialogManager()
71 {
72     ANS_LOGD("enter");
73 }
74 
Init()75 bool NotificationDialogManager::Init()
76 {
77     ANS_LOGD("enter");
78 
79     dialogEventSubscriber = NotificationDialogEventSubscriber::Create(*this);
80     if (!EventFwk::CommonEventManager::SubscribeCommonEvent(dialogEventSubscriber)) {
81         ANS_LOGE("SubscribeCommonEvent Failed.");
82         dialogEventSubscriber = nullptr;
83         return false;
84     }
85     return true;
86 }
87 
RequestEnableNotificationDailog(const sptr<NotificationBundleOption> & bundle,const sptr<AnsDialogCallback> & callback,const sptr<IRemoteObject> & callerToken)88 ErrCode NotificationDialogManager::RequestEnableNotificationDailog(
89     const sptr<NotificationBundleOption>& bundle,
90     const sptr<AnsDialogCallback>& callback,
91     const sptr<IRemoteObject>& callerToken)
92 {
93     if (bundle == nullptr || callback == nullptr) {
94         return ERROR_INTERNAL_ERROR;
95     }
96     if (!AddDialogInfoIfNotExist(bundle, callback)) {
97         ANS_LOGE("AddDialogIfNotExist failed. Dialog already exists.");
98         return ERR_ANS_DIALOG_IS_POPPING;
99     }
100     ErrCode result = NotificationDialog::StartEnableNotificationDialogAbility(
101         NotificationDialogManager::NOTIFICATION_DIALOG_SERVICE_BUNDLE,
102         NotificationDialogManager::NOTIFICATION_DIALOG_SERVICE_ABILITY,
103         bundle->GetUid(),
104         bundle->GetBundleName(),
105         callerToken);
106     if (result != ERR_OK) {
107         ANS_LOGE("StartEnableNotificationDialogAbility failed, result = %{public}d", result);
108         std::unique_ptr<NotificationDialogManager::DialogInfo> dialogInfoRemoved = nullptr;
109         RemoveDialogInfoByBundleOption(bundle, dialogInfoRemoved);
110     }
111     return result;
112 }
113 
OnBundleEnabledStatusChanged(DialogStatus status,const std::string & bundleName,const int32_t & uid)114 ErrCode NotificationDialogManager::OnBundleEnabledStatusChanged(
115     DialogStatus status, const std::string& bundleName, const int32_t& uid)
116 {
117     ANS_LOGD("enter");
118     bool result = false;
119     switch (status) {
120         case DialogStatus::ALLOW_CLICKED:
121             result = OnDialogButtonClicked(bundleName, uid, true);
122             break;
123         case DialogStatus::DENY_CLICKED:
124             result = OnDialogButtonClicked(bundleName, uid, false);
125             break;
126         case DialogStatus::DIALOG_CRASHED:
127             result = OnDialogCrashed(bundleName, uid);
128             break;
129         case DialogStatus::DIALOG_SERVICE_DESTROYED:
130             result = OnDialogServiceDestroyed();
131             break;
132         case DialogStatus::REMOVE_BUNDLE:
133             result = onRemoveBundle(bundleName, uid);
134             break;
135         default:
136             result = false;
137     }
138     if (!result) {
139         ANS_LOGE("OnBundleEnabledStatusChanged failed");
140         return ERROR_INTERNAL_ERROR;
141     }
142     return ERR_OK;
143 }
144 
AddDialogInfo(const sptr<NotificationBundleOption> & bundle,const sptr<AnsDialogCallback> & callback)145 ErrCode NotificationDialogManager::AddDialogInfo(const sptr<NotificationBundleOption>& bundle,
146     const sptr<AnsDialogCallback>& callback)
147 {
148     if (!AddDialogInfoIfNotExist(bundle, callback)) {
149         return ERR_ANS_DIALOG_IS_POPPING;
150     }
151     return ERR_OK;
152 }
153 
AddDialogInfoIfNotExist(const sptr<NotificationBundleOption> & bundle,const sptr<AnsDialogCallback> & callback)154 bool NotificationDialogManager::AddDialogInfoIfNotExist(
155     const sptr<NotificationBundleOption>& bundle,
156     const sptr<AnsDialogCallback>& callback)
157 {
158     std::lock_guard<std::mutex> lock(dialogsMutex_);
159     std::string name = bundle->GetBundleName();
160     int32_t uid = bundle->GetUid();
161     auto dialogIter = std::find_if(dialogsOpening_.begin(), dialogsOpening_.end(),
162         [&](const std::unique_ptr<DialogInfo>& dialogInfo) {
163             return dialogInfo->bundleOption->GetBundleName() == name && dialogInfo->bundleOption->GetUid() == uid;
164         });
165     if (dialogIter != dialogsOpening_.end()) {
166         return false;
167     }
168     auto dialogInfo = std::make_unique<DialogInfo>();
169     dialogInfo->bundleOption = bundle;
170     dialogInfo->callback = callback;
171     dialogsOpening_.push_back(std::move(dialogInfo));
172     return true;
173 }
174 
GetBundleOptionByBundleName(const std::string & bundleName,const int32_t & uid)175 sptr<NotificationBundleOption> NotificationDialogManager::GetBundleOptionByBundleName(
176     const std::string& bundleName, const int32_t& uid)
177 {
178     std::lock_guard<std::mutex> lock(dialogsMutex_);
179     auto dialogIter = std::find_if(dialogsOpening_.begin(), dialogsOpening_.end(),
180         [&](const std::unique_ptr<DialogInfo>& dialogInfo) {
181             return dialogInfo->bundleOption->GetBundleName() == bundleName && dialogInfo->bundleOption->GetUid() == uid;
182         });
183     if (dialogIter == dialogsOpening_.end()) {
184         return nullptr;
185     }
186     auto result = sptr<NotificationBundleOption>::MakeSptr(*((*dialogIter)->bundleOption));
187     return result;
188 };
189 
RemoveDialogInfoByBundleOption(const sptr<NotificationBundleOption> & bundle,std::unique_ptr<DialogInfo> & dialogInfoRemoved)190 void NotificationDialogManager::RemoveDialogInfoByBundleOption(const sptr<NotificationBundleOption>& bundle,
191     std::unique_ptr<DialogInfo>& dialogInfoRemoved)
192 {
193     std::lock_guard<std::mutex> lock(dialogsMutex_);
194     std::string name = bundle->GetBundleName();
195     int32_t uid = bundle->GetUid();
196     auto dialogIter = std::find_if(dialogsOpening_.begin(), dialogsOpening_.end(),
197         [&](const std::unique_ptr<DialogInfo>& dialogInfo) {
198             return dialogInfo->bundleOption->GetBundleName() == name && dialogInfo->bundleOption->GetUid() == uid;
199         });
200     if (dialogIter == dialogsOpening_.end()) {
201         dialogInfoRemoved = nullptr;
202         return;
203     }
204     dialogInfoRemoved = std::move(*dialogIter);
205     dialogsOpening_.erase(dialogIter);
206 }
207 
RemoveAllDialogInfos(std::list<std::unique_ptr<DialogInfo>> & dialogInfosRemoved)208 void NotificationDialogManager::RemoveAllDialogInfos(std::list<std::unique_ptr<DialogInfo>>& dialogInfosRemoved)
209 {
210     std::lock_guard<std::mutex> lock(dialogsMutex_);
211     for (auto& dialogInfo : dialogsOpening_) {
212         dialogInfosRemoved.push_back(std::move(dialogInfo));
213     }
214     dialogsOpening_.clear();
215 }
216 
SetHasPoppedDialog(const sptr<NotificationBundleOption> & bundleOption,bool hasPopped)217 bool NotificationDialogManager::SetHasPoppedDialog(
218     const sptr<NotificationBundleOption>& bundleOption, bool hasPopped)
219 {
220     ANS_LOGD("enter");
221     if (bundleOption == nullptr) {
222         return false;
223     }
224     ErrCode result = NotificationPreferences::GetInstance()->SetHasPoppedDialog(bundleOption, hasPopped);
225     return result == ERR_OK;
226 }
227 
OnDialogButtonClicked(const std::string & bundleName,const int32_t & uid,bool enabled)228 bool NotificationDialogManager::OnDialogButtonClicked(const std::string& bundleName, const int32_t& uid, bool enabled)
229 {
230     ANS_LOGD("enter");
231     auto bundleOption = GetBundleOptionByBundleName(bundleName, uid);
232     if (bundleOption == nullptr) {
233         return false;
234     }
235 
236     NotificationDialogManager::SetHasPoppedDialog(bundleOption, true);
237 
238     ErrCode result = ans_.SetNotificationsEnabledForSpecialBundle(
239         NotificationDialogManager::DEFAULT_DEVICE_ID,
240         bundleOption, enabled);
241     if (result != ERR_OK) {
242         ANS_LOGE("SetNotificationsEnabledForSpecialBundle Failed, code is %{public}d", result);
243         // Do not return here, need to clear the data
244     }
245     EnabledDialogStatus status = enabled ? EnabledDialogStatus::ALLOW_CLICKED : EnabledDialogStatus::DENY_CLICKED;
246     return HandleOneDialogClosed(bundleOption, status);
247 }
248 
OnDialogCrashed(const std::string & bundleName,const int32_t & uid)249 bool NotificationDialogManager::OnDialogCrashed(const std::string& bundleName, const int32_t& uid)
250 {
251     ANS_LOGD("enter");
252     auto bundleOption = GetBundleOptionByBundleName(bundleName, uid);
253     if (bundleOption == nullptr) {
254         return false;
255     }
256 
257     ErrCode result = ans_.SetNotificationsEnabledForSpecialBundle(
258         NotificationDialogManager::DEFAULT_DEVICE_ID,
259         bundleOption, false);
260     if (result != ERR_OK) {
261         ANS_LOGE("SetNotificationsEnabledForSpecialBundle Failed, code is %{public}d", result);
262         // Do not return here, need to clear the data
263     }
264     return HandleOneDialogClosed(bundleOption, EnabledDialogStatus::CRASHED);
265 }
266 
OnDialogServiceDestroyed()267 bool NotificationDialogManager::OnDialogServiceDestroyed()
268 {
269     ANS_LOGD("enter");
270     return HandleAllDialogsClosed();
271 }
272 
onRemoveBundle(const std::string bundleName,const int32_t & uid)273 bool NotificationDialogManager::onRemoveBundle(const std::string bundleName, const int32_t& uid)
274 {
275     auto bundleOption = GetBundleOptionByBundleName(bundleName, uid);
276     if (bundleOption == nullptr) {
277         ANS_LOGE("onRemoveBundle bundle is null. bundleName = %{public}s", bundleName.c_str());
278         return false;
279     }
280     std::unique_ptr<NotificationDialogManager::DialogInfo> dialogInfoRemoved = nullptr;
281     RemoveDialogInfoByBundleOption(bundleOption, dialogInfoRemoved);
282     return true;
283 }
284 
HandleOneDialogClosed(sptr<NotificationBundleOption> bundleOption,EnabledDialogStatus status)285 bool NotificationDialogManager::HandleOneDialogClosed(
286     sptr<NotificationBundleOption> bundleOption,
287     EnabledDialogStatus status)
288 {
289     if (bundleOption == nullptr) {
290         return false;
291     }
292     std::unique_ptr<DialogInfo> dialogInfoRemoved = nullptr;
293     RemoveDialogInfoByBundleOption(bundleOption, dialogInfoRemoved);
294     if (dialogInfoRemoved != nullptr && dialogInfoRemoved->callback != nullptr) {
295         DialogStatusData statusData(status);
296         dialogInfoRemoved->callback->OnDialogStatusChanged(statusData);
297     }
298     return true;
299 }
300 
HandleAllDialogsClosed()301 bool NotificationDialogManager::HandleAllDialogsClosed()
302 {
303     std::list<std::unique_ptr<DialogInfo>> dialogInfosRemoved;
304     RemoveAllDialogInfos(dialogInfosRemoved);
305     for (auto& dialogInfoSP : dialogInfosRemoved) {
306         if (dialogInfoSP != nullptr && dialogInfoSP->callback != nullptr) {
307             DialogStatusData statusData(EnabledDialogStatus::CRASHED);
308             dialogInfoSP->callback->OnDialogStatusChanged(statusData);
309         }
310     }
311     return true;
312 }
313 
314 } // namespace OHOS::Notification
315