123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/system/human_presence/snooping_protection_notification_blocker.h"
- #include <string>
- #include "ash/constants/ash_pref_names.h"
- #include "ash/public/cpp/notification_utils.h"
- #include "ash/public/cpp/session/session_observer.h"
- #include "ash/public/cpp/system_tray_client.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/root_window_controller.h"
- #include "ash/session/session_controller_impl.h"
- #include "ash/shell.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "ash/system/human_presence/human_presence_metrics.h"
- #include "ash/system/human_presence/snooping_protection_controller.h"
- #include "ash/system/human_presence/snooping_protection_notification_blocker_internal.h"
- #include "ash/system/model/system_tray_model.h"
- #include "ash/system/network/sms_observer.h"
- #include "ash/system/status_area_widget.h"
- #include "ash/system/unified/unified_system_tray.h"
- #include "base/bind.h"
- #include "base/check_op.h"
- #include "base/memory/weak_ptr.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/no_destructor.h"
- #include "base/notreached.h"
- #include "base/strings/string_util.h"
- #include "chromeos/ash/components/dbus/hps/hps_service.pb.h"
- #include "components/prefs/pref_change_registrar.h"
- #include "components/prefs/pref_service.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/message_center/message_center.h"
- #include "ui/message_center/public/cpp/notification.h"
- namespace ash {
- namespace {
- namespace metrics = ash::snooping_protection_metrics;
- constexpr char kNotifierId[] = "hps-notify";
- // Returns the capitalized version of an improper-noun notifier title, or the
- // unchanged title if it is a proper noun.
- std::u16string GetCapitalizedNotifierTitle(const std::u16string& title) {
- static base::NoDestructor<std::map<std::u16string, std::u16string>>
- kCapitalizedTitles(
- {{l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_APP_TITLE_LOWER),
- l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_APP_TITLE_UPPER)},
- {l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SYSTEM_TITLE_LOWER),
- l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SYSTEM_TITLE_UPPER)},
- {l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_WEB_TITLE_LOWER),
- l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_WEB_TITLE_UPPER)}});
- const auto it = kCapitalizedTitles->find(title);
- return it == kCapitalizedTitles->end() ? title : it->second;
- }
- } // namespace
- namespace hps_internal {
- // Returns the popup message listing the correct notifier titles and the correct
- // number of titles.
- std::u16string GetTitlesBlockedMessage(
- const std::vector<std::u16string>& titles) {
- DCHECK(!titles.empty());
- const std::u16string& first_title = GetCapitalizedNotifierTitle(titles[0]);
- switch (titles.size()) {
- case 1:
- return l10n_util::GetStringFUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_1, first_title);
- case 2:
- return l10n_util::GetStringFUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_2, first_title,
- titles[1]);
- default:
- return l10n_util::GetStringFUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_3_PLUS,
- first_title, titles[1]);
- }
- }
- } // namespace hps_internal
- SnoopingProtectionNotificationBlocker::SnoopingProtectionNotificationBlocker(
- message_center::MessageCenter* message_center,
- SnoopingProtectionController* controller)
- : NotificationBlocker(message_center),
- message_center_(message_center),
- controller_(controller) {
- controller_observation_.Observe(controller_);
- // Session controller is instantiated before us in the shell.
- SessionControllerImpl* session_controller =
- Shell::Get()->session_controller();
- DCHECK(session_controller);
- session_observation_.Observe(session_controller);
- message_center_observation_.Observe(message_center_);
- UpdateInfoNotificationIfNecessary();
- }
- SnoopingProtectionNotificationBlocker::
- ~SnoopingProtectionNotificationBlocker() = default;
- void SnoopingProtectionNotificationBlocker::OnActiveUserPrefServiceChanged(
- PrefService* pref_service) {
- OnBlockingActiveChanged();
- // Listen to changes to the user's preferences.
- pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
- pref_change_registrar_->Init(pref_service);
- pref_change_registrar_->Add(
- prefs::kSnoopingProtectionNotificationSuppressionEnabled,
- base::BindRepeating(
- &SnoopingProtectionNotificationBlocker::OnBlockingPrefChanged,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void SnoopingProtectionNotificationBlocker::OnBlockingActiveChanged() {
- NotifyBlockingStateChanged();
- UpdateInfoNotificationIfNecessary();
- }
- void SnoopingProtectionNotificationBlocker::OnBlockingPrefChanged() {
- DCHECK(pref_change_registrar_);
- DCHECK(pref_change_registrar_->prefs());
- const bool pref_enabled = pref_change_registrar_->prefs()->GetBoolean(
- prefs::kSnoopingProtectionNotificationSuppressionEnabled);
- base::UmaHistogramBoolean(
- metrics::kNotificationSuppressionEnabledHistogramName, pref_enabled);
- OnBlockingActiveChanged();
- }
- bool SnoopingProtectionNotificationBlocker::ShouldShowNotificationAsPopup(
- const message_center::Notification& notification) const {
- // If we've populated our info popup, we're definitely hiding some other
- // notifications and need to inform the user.
- if (notification.id() == kInfoNotificationId)
- return true;
- // We don't suppress any popups while inactive.
- if (!BlockingActive())
- return true;
- // Always show important system notifications.
- // An exception: SMS content is personal information.
- return notification.notifier_id().type ==
- message_center::NotifierType::SYSTEM_COMPONENT &&
- !base::StartsWith(notification.id(), SmsObserver::kNotificationPrefix);
- }
- void SnoopingProtectionNotificationBlocker::OnSnoopingStatusChanged(
- bool /*snooper*/) {
- // Need to reevaluate blocking for (i.e. un/hide) all notifications when a
- // snooper appears. This also catches disabling the snooping feature all
- // together, since that is translated to a "no snooper" event by the
- // controller.
- OnBlockingActiveChanged();
- }
- void SnoopingProtectionNotificationBlocker::
- OnSnoopingProtectionControllerDestroyed() {
- controller_observation_.Reset();
- }
- void SnoopingProtectionNotificationBlocker::OnNotificationAdded(
- const std::string& notification_id) {
- if (notification_id != kInfoNotificationId)
- UpdateInfoNotificationIfNecessary();
- }
- void SnoopingProtectionNotificationBlocker::OnNotificationRemoved(
- const std::string& notification_id,
- bool /*by_user*/) {
- if (notification_id == kInfoNotificationId)
- info_popup_exists_ = false;
- else
- UpdateInfoNotificationIfNecessary();
- }
- void SnoopingProtectionNotificationBlocker::OnNotificationUpdated(
- const std::string& notification_id) {
- if (notification_id != kInfoNotificationId)
- UpdateInfoNotificationIfNecessary();
- }
- void SnoopingProtectionNotificationBlocker::OnBlockingStateChanged(
- message_center::NotificationBlocker* blocker) {
- if (blocker != this)
- UpdateInfoNotificationIfNecessary();
- }
- void SnoopingProtectionNotificationBlocker::Close(bool by_user) {}
- void SnoopingProtectionNotificationBlocker::Click(
- const absl::optional<int>& button_index,
- const absl::optional<std::u16string>& reply) {
- if (!button_index.has_value())
- return;
- switch (button_index.value()) {
- // Show notifications button
- case 0:
- Shell::Get()
- ->GetPrimaryRootWindowController()
- ->GetStatusAreaWidget()
- ->unified_system_tray()
- ->ShowBubble();
- break;
- // Show privacy settings button
- case 1:
- Shell::Get()->system_tray_model()->client()->ShowSmartPrivacySettings();
- break;
- default:
- NOTREACHED() << "Unknown button index value";
- }
- }
- bool SnoopingProtectionNotificationBlocker::BlockingActive() const {
- // Never block if the feature is disabled.
- const PrefService* const pref_service =
- Shell::Get()->session_controller()->GetActivePrefService();
- if (!pref_service ||
- !pref_service->GetBoolean(
- prefs::kSnoopingProtectionNotificationSuppressionEnabled))
- return false;
- // Only block when there isn't a snooper detected.
- return controller_->SnooperPresent();
- }
- void SnoopingProtectionNotificationBlocker::
- UpdateInfoNotificationIfNecessary() {
- // Collect the IDs whose popups would be shown but for us.
- std::set<std::string> new_blocked_popups;
- if (BlockingActive()) {
- const message_center::NotificationList::PopupNotifications popup_list =
- message_center_->GetPopupNotificationsWithoutBlocker(*this);
- for (const auto* value : popup_list) {
- if (!ShouldShowNotificationAsPopup(*value))
- new_blocked_popups.insert(value->id());
- }
- }
- if (blocked_popups_ == new_blocked_popups)
- return;
- blocked_popups_ = new_blocked_popups;
- if (blocked_popups_.empty()) {
- // No-op if the user has already closed the popup.
- message_center_->RemoveNotification(kInfoNotificationId, /*by_user=*/false);
- info_popup_exists_ = false;
- } else if (info_popup_exists_) {
- message_center_->UpdateNotification(kInfoNotificationId,
- CreateInfoNotification());
- message_center_->ResetSinglePopup(kInfoNotificationId);
- } else {
- message_center_->AddNotification(CreateInfoNotification());
- message_center_->ResetSinglePopup(kInfoNotificationId);
- info_popup_exists_ = true;
- }
- }
- std::unique_ptr<message_center::Notification>
- SnoopingProtectionNotificationBlocker::CreateInfoNotification() const {
- // Create a list of popup titles in descending order of recentness and with no
- // duplicates.
- std::vector<std::u16string> titles;
- std::set<std::u16string> seen_titles;
- for (const message_center::Notification* notification :
- message_center_->GetPopupNotificationsWithoutBlocker(*this)) {
- const std::string& id = notification->id();
- if (blocked_popups_.find(id) == blocked_popups_.end())
- continue;
- // Use a human readable-title (e.g. "Web" vs "https://somesite.com:443").
- const std::u16string& title =
- hps_internal::GetNotifierTitle<apps::AppRegistryCacheWrapper>(
- notification->notifier_id(),
- Shell::Get()->session_controller()->GetActiveAccountId());
- if (seen_titles.find(title) != seen_titles.end())
- continue;
- titles.push_back(title);
- seen_titles.insert(title);
- }
- // TODO(1276903): find out how to make the correct notification count.
- message_center::RichNotificationData notification_data;
- notification_data.buttons.push_back(
- message_center::ButtonInfo(l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SHOW_BUTTON_TEXT)));
- notification_data.buttons.push_back(
- message_center::ButtonInfo(l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SETTINGS_BUTTON_TEXT)));
- auto notification = CreateSystemNotification(
- message_center::NOTIFICATION_TYPE_SIMPLE, kInfoNotificationId,
- l10n_util::GetStringUTF16(
- IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_TITLE),
- hps_internal::GetTitlesBlockedMessage(titles),
- /*display_source=*/std::u16string(), /*origin_url=*/GURL(),
- message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
- kNotifierId,
- NotificationCatalogName::kHPSNotify),
- notification_data,
- base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
- weak_ptr_factory_.GetWeakPtr()),
- kSystemTraySnoopingProtectionIcon,
- message_center::SystemNotificationWarningLevel::NORMAL);
- return notification;
- }
- } // namespace ash
|