chromium/ash/system/notification_center/session_state_notification_blocker.cc

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/notification_center/session_state_notification_blocker.h"

#include "ash/public/cpp/message_center/oobe_notification_constants.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/do_not_disturb_notification_controller.h"
#include "ash/system/lock_screen_notification_controller.h"
#include "ash/system/power/battery_notification.h"
#include "base/containers/contains.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_types.h"
#include "ui/message_center/public/cpp/notifier_id.h"

using session_manager::SessionState;

namespace ash {

namespace {

constexpr base::TimeDelta kLoginNotificationDelay = base::Seconds(6);

// Set to false for tests so notifications can be generated without a delay.
bool g_use_login_delay_for_test = true;

bool CalculateShouldShowNotification() {
  SessionControllerImpl* const session_controller =
      Shell::Get()->session_controller();

  SessionState state = session_controller->GetSessionState();
  static const SessionState kNotificationBlockedStates[] = {
      SessionState::OOBE, SessionState::LOGIN_PRIMARY,
      SessionState::LOGIN_SECONDARY, SessionState::LOGGED_IN_NOT_ACTIVE};

  // Do not show notifications in kiosk mode or before session starts.
  if (session_controller->IsRunningInAppMode() ||
      base::Contains(kNotificationBlockedStates, state)) {
    return false;
  }

  // Do not show notifications on the lockscreen.
  if (state == SessionState::LOCKED) {
    return false;
  }

  return true;
}

bool CalculateShouldShowPopup() {
  SessionControllerImpl* const session_controller =
      Shell::Get()->session_controller();

  if (session_controller->IsRunningInAppMode() ||
      session_controller->GetSessionState() != SessionState::ACTIVE) {
    return false;
  }

  const UserSession* active_user_session =
      session_controller->GetUserSession(0);
  return active_user_session && session_controller->GetUserPrefServiceForUser(
                                    active_user_session->user_info.account_id);
}

bool IsAllowedDuringOOBE(std::string_view notification_id) {
  static const std::string_view kAllowedSystemNotificationIDs[] = {
      BatteryNotification::kNotificationId};
  static const std::string_view kAllowedProfileBoundNotificationIDs[] = {
      kOOBELocaleSwitchNotificationId, kOOBEGnubbyNotificationId};

  for (const auto& id : kAllowedSystemNotificationIDs) {
    if (notification_id == id) {
      return true;
    }
  }

  // Check here not for a full name equivalence, but for a substring existence
  // because profile-bound notifications have a profile-specific prefix added
  // to them.
  for (const auto& id : kAllowedProfileBoundNotificationIDs) {
    if (base::Contains(notification_id, id)) {
      return true;
    }
  }

  return false;
}

}  // namespace

SessionStateNotificationBlocker::SessionStateNotificationBlocker(
    message_center::MessageCenter* message_center)
    : NotificationBlocker(message_center),
      should_show_notification_(CalculateShouldShowNotification()),
      should_show_popup_(CalculateShouldShowPopup()) {
  Shell::Get()->session_controller()->AddObserver(this);
}

SessionStateNotificationBlocker::~SessionStateNotificationBlocker() {
  Shell::Get()->session_controller()->RemoveObserver(this);
}

// static
void SessionStateNotificationBlocker::SetUseLoginNotificationDelayForTest(
    bool use_delay) {
  g_use_login_delay_for_test = use_delay;
}

void SessionStateNotificationBlocker::OnLoginTimerEnded() {
  NotifyBlockingStateChanged();
}

bool SessionStateNotificationBlocker::ShouldShowNotification(
    const message_center::Notification& notification) const {
  // Do not show non system notifications for `kLoginNotificationsDelay`
  // duration.
  if (notification.notifier_id().type !=
          message_center::NotifierType::SYSTEM_COMPONENT &&
      login_delay_timer_.IsRunning()) {
    return false;
  }

  // Never show notifications in kiosk mode.
  if (Shell::Get()->session_controller()->IsRunningInAppMode()) {
    return false;
  }

  const SessionState session_state =
      Shell::Get()->session_controller()->GetSessionState();
  // Do not show the "Do not disturb" notification if there is no active
  // session.
  if (notification.id() ==
          DoNotDisturbNotificationController::kDoNotDisturbNotificationId &&
      session_state != SessionState::ACTIVE) {
    return false;
  }

  // Allow the lock screen notification to show on and only on the lock screen.
  if (notification.id() ==
      LockScreenNotificationController::kLockScreenNotificationId) {
    return session_state == SessionState::LOCKED;
  }

  // Only allow System priority notifications to be shown on the lock screen. We
  // need to provide an exception here since by default we're blocking all
  // notifications when the screen is locked.
  if (notification.priority() ==
          message_center::NotificationPriority::SYSTEM_PRIORITY &&
      session_state == SessionState::LOCKED) {
    return true;
  }

  if (IsAllowedDuringOOBE(notification.id())) {
    return true;
  }

  return should_show_notification_;
}

bool SessionStateNotificationBlocker::ShouldShowNotificationAsPopup(
    const message_center::Notification& notification) const {
  SessionControllerImpl* const session_controller =
      Shell::Get()->session_controller();

  // Never show notifications in kiosk mode.
  if (session_controller->IsRunningInAppMode()) {
    return false;
  }

  // Do not show non system notifications for `kLoginNotificationsDelay`
  // duration.
  if (notification.notifier_id().type !=
          message_center::NotifierType::SYSTEM_COMPONENT &&
      login_delay_timer_.IsRunning()) {
    return false;
  }

  if (IsAllowedDuringOOBE(notification.id())) {
    return true;
  }

  return should_show_popup_;
}

void SessionStateNotificationBlocker::OnFirstSessionStarted() {
  if (!g_use_login_delay_for_test) {
    return;
  }
  login_delay_timer_.Start(FROM_HERE, kLoginNotificationDelay, this,
                           &SessionStateNotificationBlocker::OnLoginTimerEnded);
}

void SessionStateNotificationBlocker::OnSessionStateChanged(
    SessionState state) {
  CheckStateAndNotifyIfChanged();
}

void SessionStateNotificationBlocker::OnActiveUserPrefServiceChanged(
    PrefService* pref_service) {
  CheckStateAndNotifyIfChanged();
}

void SessionStateNotificationBlocker::CheckStateAndNotifyIfChanged() {
  const bool new_should_show_notification = CalculateShouldShowNotification();
  const bool new_should_show_popup = CalculateShouldShowPopup();
  if (new_should_show_notification == should_show_notification_ &&
      new_should_show_popup == should_show_popup_) {
    return;
  }

  should_show_notification_ = new_should_show_notification;
  should_show_popup_ = new_should_show_popup;
  NotifyBlockingStateChanged();
}

}  // namespace ash