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_