1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
ash / system / privacy_hub / privacy_hub_notification.h [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_PRIVACY_HUB_PRIVACY_HUB_NOTIFICATION_H_
#define ASH_SYSTEM_PRIVACY_HUB_PRIVACY_HUB_NOTIFICATION_H_
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "ash/ash_export.h"
#include "ash/public/cpp/system_notification_builder.h"
#include "ash/system/privacy_hub/sensor_disabled_notification_delegate.h"
#include "ui/message_center/message_center_observer.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
namespace ash {
// A custom delegate that ensures consistent handling of notification
// interactions across all Privacy Hub notifications.
class ASH_EXPORT PrivacyHubNotificationClickDelegate
: public message_center::NotificationDelegate {
public:
// The `button_callback` will be executed when the only button of the
// notification is clicked.
explicit PrivacyHubNotificationClickDelegate(
base::RepeatingClosure button_click);
// message_center::NotificationDelegate:
void Click(const std::optional<int>& button_index,
const std::optional<std::u16string>& reply) override;
// When clicking on the notification message execute this `callback`.
void SetMessageClickCallback(base::RepeatingClosure callback);
// Set the `callback` for an additional button.
void SetSecondButtonCallback(base::RepeatingClosure callback);
private:
~PrivacyHubNotificationClickDelegate() override;
// Run `callback` if it's not null. Do nothing otherwise.
void RunCallbackIfNotNull(const base::RepeatingClosure& callback);
std::array<base::RepeatingClosure, 2> button_callbacks_;
base::RepeatingClosure message_callback_;
};
// Represents the information displayed in a `PrivacyHubNotification`.
class ASH_EXPORT PrivacyHubNotificationDescriptor {
// `message_ids` must not be empty`.
// `message_ids.at(0)` should be the generic notification message with no
// application names.
// `message_ids.at(n)` should be the notification message with `n` application
// names.
// `delegate` specifies the callback to be used when the button is clicked.
// if it is null (default), an action that sets all privacy toggles
// corresponding `sensors` to true.
public:
PrivacyHubNotificationDescriptor(
const SensorDisabledNotificationDelegate::SensorSet& sensors,
int title_id,
const std::vector<int>& button_ids,
const std::vector<int>& message_ids,
scoped_refptr<PrivacyHubNotificationClickDelegate> delegate = nullptr);
PrivacyHubNotificationDescriptor(
const PrivacyHubNotificationDescriptor& other);
PrivacyHubNotificationDescriptor& operator=(
const PrivacyHubNotificationDescriptor& other);
~PrivacyHubNotificationDescriptor();
const std::vector<int>& button_ids() const { return button_ids_; }
const SensorDisabledNotificationDelegate::SensorSet& sensors() const {
return sensors_;
}
const std::vector<int>& message_ids() const { return message_ids_; }
scoped_refptr<PrivacyHubNotificationClickDelegate> delegate() const {
return delegate_;
}
int title_id_;
private:
std::vector<int> button_ids_;
SensorDisabledNotificationDelegate::SensorSet sensors_;
std::vector<int> message_ids_;
scoped_refptr<PrivacyHubNotificationClickDelegate> delegate_;
};
// This class wraps `SystemNotificationBuilder` and adds additional constraints
// and shared behavior that applies to all Privacy Hub notifications.
class ASH_EXPORT PrivacyHubNotification
: public message_center::MessageCenterObserver {
public:
// Class used to encapsulate the logic whether a notification should be
// throttled (suppressed because it has been manually dismissed recently);
class Throttler {
public:
Throttler() = default;
Throttler(const Throttler&) = delete;
Throttler& operator=(const Throttler&) = delete;
virtual ~Throttler() = default;
// Returns `true` if the notification should be suppressed.
virtual bool ShouldThrottle() = 0;
// To be called when a notification is dismissed by the user.
virtual void RecordDismissalByUser() = 0;
};
// Create a new notification.
// When calling `Show() or `Update()`:
// If `sensors_` is empty, the generic notification message from `descriptor`
// will be displayed.
// If `sensors_` is non-empty and `n` applications are using the sensors in
// `sensors_`, the displayed notification message will contain `n` application
// names. If `descriptor` does not contain a notification message with `n`
// application names, the generic notification message from `descriptor` will
// be displayed.
PrivacyHubNotification(const std::string& id,
NotificationCatalogName catalog_name,
const PrivacyHubNotificationDescriptor& descriptor);
// When PrivacyHubNotification is constructed with multiple
// `PrivacyHubNotificationDescriptor`s, which descriptor to use will be
// decided depending on the value of `sensors_`. When `sensors_` changes, the
// descriptor to use will also change.
//`descriptors` must have multiple `PrivacyHubNotificationDescriptor` objects,
// use the previous constructor otherwise please.
PrivacyHubNotification(
const std::string& id,
NotificationCatalogName catalog_name,
const std::vector<PrivacyHubNotificationDescriptor>& descriptors);
PrivacyHubNotification(PrivacyHubNotification&&) = delete;
PrivacyHubNotification& operator=(PrivacyHubNotification&&) = delete;
~PrivacyHubNotification() override;
// message_center::MessageCenterObserver:
void OnNotificationRemoved(const std::string& notification_id,
bool by_user) override;
// Show the notification to the user for at least `kMinShowTime`. Every time
// `Show()` is called, the notification will pop up. For silent updates, use
// the `Update()` function.
void Show();
// Hide the notification from the message center.
void Hide();
// Returns true if this notificaiton is shown (present in the message center).
bool IsShown();
// Silently updates the notification when needed, for example, when an
// application stops accessing a sensor and the name of that application needs
// to be removed from the notification without letting the notification pop up
// again.
void Update();
// Updates priority for notification that will be created via Show/Update.
void SetPriority(message_center::NotificationPriority priority);
// Updates the value of `sensors_`.
void SetSensors(SensorDisabledNotificationDelegate::SensorSet sensors);
// Get the underlying `SystemNotificationBuilder` to do modifications beyond
// what this wrapper allows you to do. If you change the ID of the message
// `Show()` and `Hide()` are not going to work reliably.
SystemNotificationBuilder& builder() { return builder_; }
// Sets a custom Throttler - the object that decides whether to suppress a
// notification due too too many repetitions.
void SetThrottler(std::unique_ptr<Throttler> throttler);
private:
// Starts observation of dismissed messages
void StartDismissalObservation();
// Stops observation of dismissed messages
void StopDismissalObservation();
// Get names of apps accessing sensors in `sensors_`. At most `number_of_apps`
// elements will be returned.
std::vector<std::u16string> GetAppsAccessingSensors(
size_t number_of_apps) const;
// Propagates information about the update in notification content (message,
// title, buttons etc.) to the underlying `SystemNotificationBuilder`. This is
// always done before showing or updating a notification.
void SetNotificationContent();
std::string id_;
// A set of `PrivacyHubNotificationDescriptor`s. Appropriate
// `PrivacyHubNotificationDescriptor` for a specific `SensorSet` can be found
// using the standard `find` function. `sensors_.ToEnumBitmask()` can be used
// as the key for the `find` function.
std::set<PrivacyHubNotificationDescriptor, std::less<>>
notification_descriptors_;
SensorDisabledNotificationDelegate::SensorSet sensors_;
// `notification_descriptors_` is a set of
// `PrivacyHubNotificationDescriptor`s. The content in the descriptors are
// used to update the underlying `SystemNotificationBuilder`. Before a call to
// `Show()` or `Update()`, the underlying builder needs to be updated. Content
// of which descriptor to use to update the builder depends on the value
// current value of `sensors_` enumset.
// `has_sensors_changed_` being true means that `sensors_` was updated but the
// underlying builder was not updated after that.
bool has_sensors_changed_ = true;
SystemNotificationBuilder builder_;
// TODO(b/271809217): Refactor camera HW switch notification implementation
// Notification for the camera hardware switch is currently using only a
// subset of `PrivacyHubNotification` properties. `catalog_name_` is stored to
// determine if the notification is for the camera hardware switch to handle
// it specially.
NotificationCatalogName catalog_name_;
// Encapsulates the throttling logic for this notification.
std::unique_ptr<Throttler> throttler_;
};
} // namespace ash
#endif // ASH_SYSTEM_PRIVACY_HUB_PRIVACY_HUB_NOTIFICATION_H_