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