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. bundle = %{public}s",
98             bundle->GetBundleName().c_str());
99         return ERR_ANS_DIALOG_IS_POPPING;
100     }
101     ErrCode result = NotificationDialog::StartEnableNotificationDialogAbility(
102         NotificationDialogManager::NOTIFICATION_DIALOG_SERVICE_BUNDLE,
103         NotificationDialogManager::NOTIFICATION_DIALOG_SERVICE_ABILITY,
104         bundle->GetUid(),
105         bundle->GetBundleName(),
106         callerToken);
107     if (result != ERR_OK) {
108         ANS_LOGE("StartEnableNotificationDialogAbility failed, result = %{public}d", result);
109         std::unique_ptr<NotificationDialogManager::DialogInfo> dialogInfoRemoved = nullptr;
110         RemoveDialogInfoByBundleOption(bundle, dialogInfoRemoved);
111     }
112     return result;
113 }
114 
OnBundleEnabledStatusChanged( DialogStatus status, const std::string& bundleName, const int32_t& uid)115 ErrCode NotificationDialogManager::OnBundleEnabledStatusChanged(
116     DialogStatus status, const std::string& bundleName, const int32_t& uid)
117 {
118     ANS_LOGD("enter");
119     bool result = false;
120     switch (status) {
121         case DialogStatus::ALLOW_CLICKED:
122             result = OnDialogButtonClicked(bundleName, uid, true);
123             break;
124         case DialogStatus::DENY_CLICKED:
125             result = OnDialogButtonClicked(bundleName, uid, false);
126             break;
127         case DialogStatus::DIALOG_CRASHED:
128             result = OnDialogCrashed(bundleName, uid);
129             break;
130         case DialogStatus::DIALOG_SERVICE_DESTROYED:
131             result = OnDialogServiceDestroyed();
132             break;
133         case DialogStatus::REMOVE_BUNDLE:
134             result = onRemoveBundle(bundleName, uid);
135             break;
136         default:
137             result = false;
138     }
139     if (!result) {
140         ANS_LOGE("OnBundleEnabledStatusChanged failed");
141         return ERROR_INTERNAL_ERROR;
142     }
143     return ERR_OK;
144 }
145 
AddDialogInfo(const sptr<NotificationBundleOption>& bundle, const sptr<AnsDialogCallback>& callback)146 ErrCode NotificationDialogManager::AddDialogInfo(const sptr<NotificationBundleOption>& bundle,
147     const sptr<AnsDialogCallback>& callback)
148 {
149     if (!AddDialogInfoIfNotExist(bundle, callback)) {
150         return ERR_ANS_DIALOG_IS_POPPING;
151     }
152     return ERR_OK;
153 }
154 
AddDialogInfoIfNotExist( const sptr<NotificationBundleOption>& bundle, const sptr<AnsDialogCallback>& callback)155 bool NotificationDialogManager::AddDialogInfoIfNotExist(
156     const sptr<NotificationBundleOption>& bundle,
157     const sptr<AnsDialogCallback>& callback)
158 {
159     std::lock_guard<std::mutex> lock(dialogsMutex_);
160     std::string name = bundle->GetBundleName();
161     int32_t uid = bundle->GetUid();
162     auto dialogIter = std::find_if(dialogsOpening_.begin(), dialogsOpening_.end(),
163         [&](const std::unique_ptr<DialogInfo>& dialogInfo) {
164             return dialogInfo->bundleOption->GetBundleName() == name && dialogInfo->bundleOption->GetUid() == uid;
165         });
166     if (dialogIter != dialogsOpening_.end()) {
167         return false;
168     }
169     auto dialogInfo = std::make_unique<DialogInfo>();
170     dialogInfo->bundleOption = bundle;
171     dialogInfo->callback = callback;
172     dialogsOpening_.push_back(std::move(dialogInfo));
173     return true;
174 }
175 
GetBundleOptionByBundleName( const std::string& bundleName, const int32_t& uid)176 sptr<NotificationBundleOption> NotificationDialogManager::GetBundleOptionByBundleName(
177     const std::string& bundleName, const int32_t& uid)
178 {
179     std::lock_guard<std::mutex> lock(dialogsMutex_);
180     auto dialogIter = std::find_if(dialogsOpening_.begin(), dialogsOpening_.end(),
181         [&](const std::unique_ptr<DialogInfo>& dialogInfo) {
182             return dialogInfo->bundleOption->GetBundleName() == bundleName && dialogInfo->bundleOption->GetUid() == uid;
183         });
184     if (dialogIter == dialogsOpening_.end()) {
185         return nullptr;
186     }
187     auto result = sptr<NotificationBundleOption>::MakeSptr(*((*dialogIter)->bundleOption));
188     return result;
189 };
190 
RemoveDialogInfoByBundleOption(const sptr<NotificationBundleOption>& bundle, std::unique_ptr<DialogInfo>& dialogInfoRemoved)191 void NotificationDialogManager::RemoveDialogInfoByBundleOption(const sptr<NotificationBundleOption>& bundle,
192     std::unique_ptr<DialogInfo>& dialogInfoRemoved)
193 {
194     std::lock_guard<std::mutex> lock(dialogsMutex_);
195     std::string name = bundle->GetBundleName();
196     int32_t uid = bundle->GetUid();
197     auto dialogIter = std::find_if(dialogsOpening_.begin(), dialogsOpening_.end(),
198         [&](const std::unique_ptr<DialogInfo>& dialogInfo) {
199             return dialogInfo->bundleOption->GetBundleName() == name && dialogInfo->bundleOption->GetUid() == uid;
200         });
201     if (dialogIter == dialogsOpening_.end()) {
202         dialogInfoRemoved = nullptr;
203         return;
204     }
205     dialogInfoRemoved = std::move(*dialogIter);
206     dialogsOpening_.erase(dialogIter);
207 }
208 
RemoveAllDialogInfos(std::list<std::unique_ptr<DialogInfo>>& dialogInfosRemoved)209 void NotificationDialogManager::RemoveAllDialogInfos(std::list<std::unique_ptr<DialogInfo>>& dialogInfosRemoved)
210 {
211     std::lock_guard<std::mutex> lock(dialogsMutex_);
212     for (auto& dialogInfo : dialogsOpening_) {
213         dialogInfosRemoved.push_back(std::move(dialogInfo));
214     }
215     dialogsOpening_.clear();
216 }
217 
SetHasPoppedDialog( const sptr<NotificationBundleOption>& bundleOption, bool hasPopped)218 bool NotificationDialogManager::SetHasPoppedDialog(
219     const sptr<NotificationBundleOption>& bundleOption, bool hasPopped)
220 {
221     ANS_LOGD("enter");
222     if (bundleOption == nullptr) {
223         return false;
224     }
225     ErrCode result = NotificationPreferences::GetInstance()->SetHasPoppedDialog(bundleOption, hasPopped);
226     return result == ERR_OK;
227 }
228 
OnDialogButtonClicked(const std::string& bundleName, const int32_t& uid, bool enabled)229 bool NotificationDialogManager::OnDialogButtonClicked(const std::string& bundleName, const int32_t& uid, bool enabled)
230 {
231     ANS_LOGD("enter");
232     auto bundleOption = GetBundleOptionByBundleName(bundleName, uid);
233     if (bundleOption == nullptr) {
234         return false;
235     }
236 
237     NotificationDialogManager::SetHasPoppedDialog(bundleOption, true);
238 
239     ErrCode result = ans_.SetNotificationsEnabledForSpecialBundle(
240         NotificationDialogManager::DEFAULT_DEVICE_ID,
241         bundleOption, enabled);
242     if (result != ERR_OK) {
243         ANS_LOGE("SetNotificationsEnabledForSpecialBundle Failed, code is %{public}d", result);
244         // Do not return here, need to clear the data
245     }
246     EnabledDialogStatus status = enabled ? EnabledDialogStatus::ALLOW_CLICKED : EnabledDialogStatus::DENY_CLICKED;
247     return HandleOneDialogClosed(bundleOption, status);
248 }
249 
OnDialogCrashed(const std::string& bundleName, const int32_t& uid)250 bool NotificationDialogManager::OnDialogCrashed(const std::string& bundleName, const int32_t& uid)
251 {
252     ANS_LOGD("enter");
253     auto bundleOption = GetBundleOptionByBundleName(bundleName, uid);
254     if (bundleOption == nullptr) {
255         return false;
256     }
257 
258     ErrCode result = ans_.SetNotificationsEnabledForSpecialBundle(
259         NotificationDialogManager::DEFAULT_DEVICE_ID,
260         bundleOption, false);
261     if (result != ERR_OK) {
262         ANS_LOGE("SetNotificationsEnabledForSpecialBundle Failed, code is %{public}d", result);
263         // Do not return here, need to clear the data
264     }
265     return HandleOneDialogClosed(bundleOption, EnabledDialogStatus::CRASHED);
266 }
267 
OnDialogServiceDestroyed()268 bool NotificationDialogManager::OnDialogServiceDestroyed()
269 {
270     ANS_LOGD("enter");
271     return HandleAllDialogsClosed();
272 }
273 
onRemoveBundle(const std::string bundleName, const int32_t& uid)274 bool NotificationDialogManager::onRemoveBundle(const std::string bundleName, const int32_t& uid)
275 {
276     auto bundleOption = GetBundleOptionByBundleName(bundleName, uid);
277     if (bundleOption == nullptr) {
278         ANS_LOGE("onRemoveBundle bundle is null. bundleName = %{public}s", bundleName.c_str());
279         return false;
280     }
281     std::unique_ptr<NotificationDialogManager::DialogInfo> dialogInfoRemoved = nullptr;
282     RemoveDialogInfoByBundleOption(bundleOption, dialogInfoRemoved);
283     return true;
284 }
285 
HandleOneDialogClosed( sptr<NotificationBundleOption> bundleOption, EnabledDialogStatus status)286 bool NotificationDialogManager::HandleOneDialogClosed(
287     sptr<NotificationBundleOption> bundleOption,
288     EnabledDialogStatus status)
289 {
290     if (bundleOption == nullptr) {
291         return false;
292     }
293     std::unique_ptr<DialogInfo> dialogInfoRemoved = nullptr;
294     RemoveDialogInfoByBundleOption(bundleOption, dialogInfoRemoved);
295     if (dialogInfoRemoved != nullptr && dialogInfoRemoved->callback != nullptr) {
296         DialogStatusData statusData(status);
297         dialogInfoRemoved->callback->OnDialogStatusChanged(statusData);
298     }
299     return true;
300 }
301 
HandleAllDialogsClosed()302 bool NotificationDialogManager::HandleAllDialogsClosed()
303 {
304     std::list<std::unique_ptr<DialogInfo>> dialogInfosRemoved;
305     RemoveAllDialogInfos(dialogInfosRemoved);
306     for (auto& dialogInfoSP : dialogInfosRemoved) {
307         if (dialogInfoSP != nullptr && dialogInfoSP->callback != nullptr) {
308             DialogStatusData statusData(EnabledDialogStatus::CRASHED);
309             dialogInfoSP->callback->OnDialogStatusChanged(statusData);
310         }
311     }
312     return true;
313 }
314 
315 } // namespace OHOS::Notification
316