// 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 <memory>
#include <unordered_map>
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/message_center/oobe_notification_constants.h"
#include "ash/session/test_session_controller_client.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 "ash/system/system_notification_controller.h"
#include "ash/test/ash_test_base.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"
using base::UTF8ToUTF16;
using session_manager::SessionState;
namespace ash {
namespace {
const char kNotifierSystemPriority[] = "ash.some-high-priority-component";
} // namespace
class SessionStateNotificationBlockerTest
: public NoSessionAshTestBase,
public message_center::NotificationBlocker::Observer {
public:
SessionStateNotificationBlockerTest() = default;
SessionStateNotificationBlockerTest(
const SessionStateNotificationBlockerTest&) = delete;
SessionStateNotificationBlockerTest& operator=(
const SessionStateNotificationBlockerTest&) = delete;
~SessionStateNotificationBlockerTest() override = default;
// tests::AshTestBase overrides:
void SetUp() override {
NoSessionAshTestBase::SetUp();
blocker_ = std::make_unique<SessionStateNotificationBlocker>(
message_center::MessageCenter::Get());
blocker_->Init();
blocker_->AddObserver(this);
}
void TearDown() override {
blocker_->RemoveObserver(this);
blocker_.reset();
NoSessionAshTestBase::TearDown();
}
// message_center::NotificationBlocker::Observer overrides:
void OnBlockingStateChanged(
message_center::NotificationBlocker* blocker) override {
state_changed_count_++;
}
int GetStateChangedCountAndReset() {
int result = state_changed_count_;
state_changed_count_ = 0;
return result;
}
bool ShouldShowNotification(message_center::Notification* notification) {
return blocker_->ShouldShowNotification(*notification);
}
bool ShouldShowNotification(const message_center::NotifierId& notifier_id) {
message_center::Notification notification(
message_center::NOTIFICATION_TYPE_SIMPLE,
GetNotificationId(notifier_id), u"chromeos-title", u"chromeos-message",
ui::ImageModel(), u"chromeos-source", GURL(), notifier_id,
message_center::RichNotificationData(), nullptr);
if (notifier_id.id == kNotifierSystemPriority)
notification.set_priority(message_center::SYSTEM_PRIORITY);
return blocker_->ShouldShowNotification(notification);
}
bool ShouldShowNotificationAsPopup(
const message_center::NotifierId& notifier_id) {
message_center::Notification notification(
message_center::NOTIFICATION_TYPE_SIMPLE,
GetNotificationId(notifier_id), u"chromeos-title", u"chromeos-message",
ui::ImageModel(), u"chromeos-source", GURL(), notifier_id,
message_center::RichNotificationData(), nullptr);
if (notifier_id.id == kNotifierSystemPriority)
notification.set_priority(message_center::SYSTEM_PRIORITY);
return blocker_->ShouldShowNotificationAsPopup(notification);
}
bool ShouldShowDoNotDisturbNotification() {
message_center::Notification notification(
message_center::NOTIFICATION_TYPE_SIMPLE,
DoNotDisturbNotificationController::kDoNotDisturbNotificationId,
u"chromeos-title", u"chromeos-message", ui::ImageModel(),
u"chromeos-source", GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT, "test-notifier",
NotificationCatalogName::kDoNotDisturb),
message_center::RichNotificationData(), nullptr);
return blocker_->ShouldShowNotification(notification);
}
bool ShouldShowOOBEAllowedNotification(const std::string& notification_id) {
return blocker_->ShouldShowNotification(
CreateDummyNotification(notification_id));
}
bool ShouldShowOOBEAllowedNotificationAsPopup(
const std::string& notification_id) {
return blocker_->ShouldShowNotificationAsPopup(
CreateDummyNotification(notification_id));
}
void SetLockedState(bool locked) {
GetSessionControllerClient()->SetSessionState(
locked ? SessionState::LOCKED : SessionState::ACTIVE);
}
LockScreenNotificationController* lock_screen_notification_controller() {
return Shell::Get()->system_notification_controller()->lock_screen_.get();
}
private:
std::string GetNotificationId(const message_center::NotifierId& notifier_id) {
return notifier_id.id == kNotifierSystemPriority
? BatteryNotification::kNotificationId
: "chromeos-id";
}
message_center::Notification CreateDummyNotification(
const std::string& notification_id) {
message_center::NotifierId notifier_id(
message_center::NotifierType::SYSTEM_COMPONENT, notification_id,
NotificationCatalogName::kTestCatalogName);
message_center::Notification notification(
message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
u"chromeos-title", u"chromeos-message", ui::ImageModel(),
u"chromeos-source", GURL(), notifier_id,
message_center::RichNotificationData(), nullptr);
return notification;
}
int state_changed_count_ = 0;
std::unique_ptr<message_center::NotificationBlocker> blocker_;
};
TEST_F(SessionStateNotificationBlockerTest, BaseTest) {
// OOBE.
GetSessionControllerClient()->SetSessionState(SessionState::OOBE);
EXPECT_EQ(0, GetStateChangedCountAndReset());
message_center::NotifierId notifier_id(
message_center::NotifierType::APPLICATION, "test-notifier");
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_FALSE(ShouldShowNotification(notifier_id));
// Login screen.
GetSessionControllerClient()->SetSessionState(SessionState::LOGIN_PRIMARY);
EXPECT_EQ(0, GetStateChangedCountAndReset());
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_FALSE(ShouldShowNotification(notifier_id));
// Logged in as a normal user.
SimulateUserLogin("[email protected]");
EXPECT_EQ(1, GetStateChangedCountAndReset());
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_TRUE(ShouldShowNotification(notifier_id));
// Lock.
SetLockedState(true);
EXPECT_EQ(1, GetStateChangedCountAndReset());
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
// Only system notifications are allowed to be shown in the notification
// center in the lock screen.
EXPECT_FALSE(ShouldShowNotification(notifier_id));
message_center::NotifierId system_notifier_id(
message_center::NotifierType::SYSTEM_COMPONENT, kNotifierSystemPriority,
NotificationCatalogName::kTestCatalogName);
EXPECT_TRUE(ShouldShowNotification(system_notifier_id));
// Unlock.
SetLockedState(false);
EXPECT_EQ(1, GetStateChangedCountAndReset());
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_TRUE(ShouldShowNotification(notifier_id));
}
TEST_F(SessionStateNotificationBlockerTest, AlwaysAllowedNotifier) {
// NOTIFIER_DISPLAY is allowed to shown in the login screen.
message_center::NotifierId notifier_id(
message_center::NotifierType::SYSTEM_COMPONENT, kNotifierSystemPriority,
NotificationCatalogName::kTestCatalogName);
// OOBE.
GetSessionControllerClient()->SetSessionState(SessionState::OOBE);
EXPECT_EQ(0, GetStateChangedCountAndReset());
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_TRUE(ShouldShowNotification(notifier_id));
// Login screen.
GetSessionControllerClient()->SetSessionState(SessionState::LOGIN_PRIMARY);
EXPECT_EQ(0, GetStateChangedCountAndReset());
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_TRUE(ShouldShowNotification(notifier_id));
// Logged in as a normal user.
SimulateUserLogin("[email protected]");
EXPECT_EQ(1, GetStateChangedCountAndReset());
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_TRUE(ShouldShowNotification(notifier_id));
// Lock.
SetLockedState(true);
EXPECT_EQ(1, GetStateChangedCountAndReset());
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_TRUE(ShouldShowNotification(notifier_id));
// Unlock.
SetLockedState(false);
EXPECT_EQ(1, GetStateChangedCountAndReset());
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_TRUE(ShouldShowNotification(notifier_id));
}
TEST_F(SessionStateNotificationBlockerTest, BlockOnPrefService) {
// OOBE.
GetSessionControllerClient()->SetSessionState(SessionState::OOBE);
EXPECT_EQ(0, GetStateChangedCountAndReset());
message_center::NotifierId notifier_id(
message_center::NotifierType::APPLICATION, "test-notifier");
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
// Login screen.
GetSessionControllerClient()->SetSessionState(SessionState::LOGIN_PRIMARY);
EXPECT_EQ(0, GetStateChangedCountAndReset());
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
// Simulates login event sequence in production code:
// - Add a user session;
// - User session is set as active session;
// - Session state changes to active;
// - User PrefService is initialized sometime later.
const AccountId kUserAccountId = AccountId::FromUserEmail("[email protected]");
TestSessionControllerClient* const session_controller_client =
GetSessionControllerClient();
session_controller_client->AddUserSession(kUserAccountId.GetUserEmail(),
user_manager::UserType::kRegular,
false /* provide_pref_service */);
EXPECT_EQ(0, GetStateChangedCountAndReset());
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
session_controller_client->SwitchActiveUser(kUserAccountId);
EXPECT_EQ(0, GetStateChangedCountAndReset());
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
session_controller_client->SetSessionState(SessionState::ACTIVE);
EXPECT_EQ(1, GetStateChangedCountAndReset());
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
session_controller_client->ProvidePrefServiceForUser(kUserAccountId);
EXPECT_EQ(1, GetStateChangedCountAndReset());
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
}
TEST_F(SessionStateNotificationBlockerTest, BlockInKioskMode) {
message_center::NotifierId notifier_id(
message_center::NotifierType::SYSTEM_COMPONENT, kNotifierSystemPriority,
NotificationCatalogName::kTestCatalogName);
EXPECT_TRUE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_TRUE(ShouldShowNotification(notifier_id));
SimulateKioskMode(user_manager::UserType::kKioskApp);
EXPECT_FALSE(ShouldShowNotificationAsPopup(notifier_id));
EXPECT_FALSE(ShouldShowNotification(notifier_id));
}
TEST_F(SessionStateNotificationBlockerTest, DelayAfterLogin) {
SessionStateNotificationBlocker::SetUseLoginNotificationDelayForTest(true);
GetSessionControllerClient()->SetSessionState(SessionState::LOGIN_PRIMARY);
// Logged in as a normal user.
SimulateUserLogin("[email protected]");
// Non system notification should not be shown immediately after login.
message_center::NotifierId notifier_id(
message_center::NotifierType::APPLICATION, "test-notifier");
EXPECT_FALSE(ShouldShowNotification(notifier_id));
// System notification should still be shown.
message_center::NotifierId system_notifier_id(
message_center::NotifierType::SYSTEM_COMPONENT, "system-notifier",
NotificationCatalogName::kTestCatalogName);
EXPECT_TRUE(ShouldShowNotification(system_notifier_id));
// The notification delay should not be enabled for all other tests.
SessionStateNotificationBlocker::SetUseLoginNotificationDelayForTest(false);
}
TEST_F(SessionStateNotificationBlockerTest, DoNotDisturbNotification) {
// OOBE.
GetSessionControllerClient()->SetSessionState(SessionState::OOBE);
EXPECT_FALSE(ShouldShowDoNotDisturbNotification());
// Login screen.
GetSessionControllerClient()->SetSessionState(SessionState::LOGIN_PRIMARY);
EXPECT_FALSE(ShouldShowDoNotDisturbNotification());
// Logged in as a normal user.
SimulateUserLogin("[email protected]");
EXPECT_TRUE(ShouldShowDoNotDisturbNotification());
// Lock.
SetLockedState(true);
EXPECT_FALSE(ShouldShowDoNotDisturbNotification());
// Unlock.
SetLockedState(false);
EXPECT_TRUE(ShouldShowDoNotDisturbNotification());
}
TEST_F(SessionStateNotificationBlockerTest, LockScreenNotification) {
// OOBE.
GetSessionControllerClient()->SetSessionState(SessionState::OOBE);
EXPECT_FALSE(ShouldShowNotification(
lock_screen_notification_controller()->CreateNotification().get()));
// Login screen.
GetSessionControllerClient()->SetSessionState(SessionState::LOGIN_PRIMARY);
EXPECT_FALSE(ShouldShowNotification(
lock_screen_notification_controller()->CreateNotification().get()));
// Logged in as a normal user.
SimulateUserLogin("[email protected]");
EXPECT_FALSE(ShouldShowNotification(
lock_screen_notification_controller()->CreateNotification().get()));
// Lock.
SetLockedState(true);
EXPECT_TRUE(ShouldShowNotification(
lock_screen_notification_controller()->CreateNotification().get()));
// Unlock.
SetLockedState(false);
EXPECT_FALSE(ShouldShowNotification(
lock_screen_notification_controller()->CreateNotification().get()));
}
TEST_F(SessionStateNotificationBlockerTest, NotificationAllowedDuringOOBE) {
const std::unordered_map<std::string, /*expected_notification_allowed=*/bool>
kTestCases = {
{BatteryNotification::kNotificationId, true},
{kOOBELocaleSwitchNotificationId, true},
{kOOBEGnubbyNotificationId, true},
{"new-fancy-notification", false},
};
const SessionState kOOBEStates[] = {SessionState::OOBE,
SessionState::LOGIN_PRIMARY,
SessionState::LOGIN_SECONDARY};
for (const auto& state : kOOBEStates) {
GetSessionControllerClient()->SetSessionState(state);
for (const auto& test_case : kTestCases) {
EXPECT_EQ(ShouldShowOOBEAllowedNotification(test_case.first),
test_case.second);
EXPECT_EQ(ShouldShowOOBEAllowedNotificationAsPopup(test_case.first),
test_case.second);
}
}
}
} // namespace ash