blob: df21b99807b6410932fb6e9ca1ca1fcd603839ce [file] [log] [blame]
// Copyright 2020 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/ambient/ambient_controller.h"
#include <memory>
#include <string>
#include <utility>
#include "ash/ambient/test/ambient_ash_test_base.h"
#include "ash/ambient/ui/ambient_container_view.h"
#include "ash/assistant/assistant_interaction_controller_impl.h"
#include "ash/assistant/model/assistant_interaction_model.h"
#include "ash/public/cpp/ambient/ambient_prefs.h"
#include "ash/public/cpp/ambient/ambient_ui_model.h"
#include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/system/power/power_status.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/time/time.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#include "chromeos/dbus/power_manager/suspend.pb.h"
#include "chromeos/services/libassistant/public/cpp/assistant_interaction_metadata.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/events/pointer_details.h"
#include "ui/events/types/event_type.h"
namespace ash {
using chromeos::assistant::AssistantInteractionMetadata;
constexpr char kUser1[] = "[email protected]";
constexpr char kUser2[] = "[email protected]";
class AmbientControllerTest : public AmbientAshTestBase {
public:
AmbientControllerTest() : AmbientAshTestBase() {}
~AmbientControllerTest() override = default;
// AmbientAshTestBase:
void SetUp() override {
AmbientAshTestBase::SetUp();
GetSessionControllerClient()->set_show_lock_screen_views(true);
}
bool IsPrefObserved(const std::string& pref_name) {
auto* pref_change_registrar =
ambient_controller()->pref_change_registrar_.get();
DCHECK(pref_change_registrar);
return pref_change_registrar->IsObserved(pref_name);
}
bool WidgetsVisible() {
const auto& views = GetContainerViews();
return views.size() > 0 &&
std::all_of(views.cbegin(), views.cend(), [](const auto* view) {
return view->GetWidget()->IsVisible();
});
}
bool AreSessionSpecificObserversBound() {
auto* ctrl = ambient_controller();
bool ui_model_bound = ctrl->ambient_ui_model_observer_.IsObserving();
bool backend_model_bound =
ctrl->ambient_backend_model_observer_.IsObserving();
bool power_manager_bound =
ctrl->power_manager_client_observer_.IsObserving();
bool fingerprint_bound = ctrl->fingerprint_observer_receiver_.is_bound();
EXPECT_EQ(ui_model_bound, backend_model_bound)
<< "observers should all have the same state";
EXPECT_EQ(ui_model_bound, power_manager_bound)
<< "observers should all have the same state";
EXPECT_EQ(ui_model_bound, fingerprint_bound)
<< "observers should all have the same state";
return ui_model_bound;
}
};
TEST_F(AmbientControllerTest, ShowAmbientScreenUponLock) {
LockScreen();
// Lockscreen will not immediately show Ambient mode.
EXPECT_FALSE(ambient_controller()->IsShown());
// Ambient mode will show after inacivity and successfully loading first
// image.
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_FALSE(GetContainerViews().empty());
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kShown);
EXPECT_TRUE(ambient_controller()->IsShown());
// Clean up.
UnlockScreen();
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest, NotShowAmbientWhenPrefNotEnabled) {
SetAmbientModeEnabled(false);
LockScreen();
// Lockscreen will not immediately show Ambient mode.
EXPECT_FALSE(ambient_controller()->IsShown());
// Ambient mode will not show after inacivity and successfully loading first
// image.
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_TRUE(GetContainerViews().empty());
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kClosed);
EXPECT_FALSE(ambient_controller()->IsShown());
// Clean up.
UnlockScreen();
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest, HideAmbientScreen) {
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_FALSE(GetContainerViews().empty());
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kShown);
EXPECT_TRUE(ambient_controller()->IsShown());
HideAmbientScreen();
FastForwardTiny();
EXPECT_TRUE(GetContainerViews().empty());
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kHidden);
// Clean up.
UnlockScreen();
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest, CloseAmbientScreenUponUnlock) {
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_FALSE(GetContainerViews().empty());
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kShown);
EXPECT_TRUE(ambient_controller()->IsShown());
UnlockScreen();
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kClosed);
EXPECT_FALSE(ambient_controller()->IsShown());
// The view should be destroyed along the widget.
FastForwardTiny();
EXPECT_TRUE(GetContainerViews().empty());
}
TEST_F(AmbientControllerTest, CloseAmbientScreenUponUnlockSecondaryUser) {
// Simulate the login screen.
ClearLogin();
SimulateUserLogin(kUser1);
SetAmbientModeEnabled(true);
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_FALSE(GetContainerViews().empty());
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kShown);
EXPECT_TRUE(ambient_controller()->IsShown());
SimulateUserLogin(kUser2);
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kClosed);
EXPECT_FALSE(ambient_controller()->IsShown());
// The view should be destroyed along the widget.
FastForwardTiny();
EXPECT_TRUE(GetContainerViews().empty());
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kClosed);
EXPECT_FALSE(ambient_controller()->IsShown());
// The view should be destroyed along the widget.
FastForwardTiny();
EXPECT_TRUE(GetContainerViews().empty());
}
TEST_F(AmbientControllerTest, NotShowAmbientWhenLockSecondaryUser) {
// Simulate the login screen.
ClearLogin();
SimulateUserLogin(kUser1);
SetAmbientModeEnabled(true);
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_FALSE(GetContainerViews().empty());
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kShown);
EXPECT_TRUE(ambient_controller()->IsShown());
SimulateUserLogin(kUser2);
SetAmbientModeEnabled(true);
// Ambient mode should not show for second user even if that user has the pref
// turned on.
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kClosed);
EXPECT_FALSE(ambient_controller()->IsShown());
// The view should be destroyed along the widget.
FastForwardTiny();
EXPECT_TRUE(GetContainerViews().empty());
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kClosed);
EXPECT_FALSE(ambient_controller()->IsShown());
// The view should be destroyed along the widget.
EXPECT_TRUE(GetContainerViews().empty());
}
TEST_F(AmbientControllerTest, ShouldRequestAccessTokenWhenLockingScreen) {
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token.
LockScreen();
EXPECT_TRUE(IsAccessTokenRequestPending());
std::string access_token = "access_token";
IssueAccessToken(access_token, /*with_error=*/false);
EXPECT_FALSE(IsAccessTokenRequestPending());
// Should close ambient widget already when unlocking screen.
UnlockScreen();
EXPECT_FALSE(IsAccessTokenRequestPending());
}
TEST_F(AmbientControllerTest, ShouldNotRequestAccessTokenWhenPrefNotEnabled) {
SetAmbientModeEnabled(false);
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will not request a token.
LockScreen();
EXPECT_FALSE(IsAccessTokenRequestPending());
UnlockScreen();
EXPECT_FALSE(IsAccessTokenRequestPending());
}
TEST_F(AmbientControllerTest, ShouldReturnCachedAccessToken) {
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token.
LockScreen();
EXPECT_TRUE(IsAccessTokenRequestPending());
std::string access_token = "access_token";
IssueAccessToken(access_token, /*with_error=*/false);
EXPECT_FALSE(IsAccessTokenRequestPending());
// Another token request will return cached token.
base::OnceClosure closure = base::MakeExpectedRunClosure(FROM_HERE);
base::RunLoop run_loop;
ambient_controller()->RequestAccessToken(base::BindLambdaForTesting(
[&](const std::string& gaia_id, const std::string& access_token_fetched) {
EXPECT_EQ(access_token_fetched, access_token);
std::move(closure).Run();
run_loop.Quit();
}));
EXPECT_FALSE(IsAccessTokenRequestPending());
run_loop.Run();
// Clean up.
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, ShouldReturnEmptyAccessToken) {
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token.
LockScreen();
EXPECT_TRUE(IsAccessTokenRequestPending());
std::string access_token = "access_token";
IssueAccessToken(access_token, /*with_error=*/false);
EXPECT_FALSE(IsAccessTokenRequestPending());
// Another token request will return cached token.
base::OnceClosure closure = base::MakeExpectedRunClosure(FROM_HERE);
base::RunLoop run_loop_1;
ambient_controller()->RequestAccessToken(base::BindLambdaForTesting(
[&](const std::string& gaia_id, const std::string& access_token_fetched) {
EXPECT_EQ(access_token_fetched, access_token);
std::move(closure).Run();
run_loop_1.Quit();
}));
EXPECT_FALSE(IsAccessTokenRequestPending());
run_loop_1.Run();
base::RunLoop run_loop_2;
// When token expired, another token request will get empty token.
constexpr base::TimeDelta kTokenRefreshDelay =
base::TimeDelta::FromSeconds(60);
task_environment()->FastForwardBy(kTokenRefreshDelay);
closure = base::MakeExpectedRunClosure(FROM_HERE);
ambient_controller()->RequestAccessToken(base::BindLambdaForTesting(
[&](const std::string& gaia_id, const std::string& access_token_fetched) {
EXPECT_TRUE(access_token_fetched.empty());
std::move(closure).Run();
run_loop_2.Quit();
}));
EXPECT_FALSE(IsAccessTokenRequestPending());
run_loop_2.Run();
// Clean up.
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenAfterFailure) {
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token.
LockScreen();
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// Token request automatically retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
// Clean up.
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenWithBackoffPolicy) {
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token.
LockScreen();
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
base::TimeDelta delay1 = GetRefreshTokenDelay();
task_environment()->FastForwardBy(delay1 * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
base::TimeDelta delay2 = GetRefreshTokenDelay();
EXPECT_GT(delay2, delay1);
task_environment()->FastForwardBy(delay2 * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
// Clean up.
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, ShouldRetryRefreshAccessTokenOnlyThreeTimes) {
EXPECT_FALSE(IsAccessTokenRequestPending());
// Lock the screen will request a token.
LockScreen();
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// 1st retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// 2nd retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// 3rd retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_TRUE(IsAccessTokenRequestPending());
IssueAccessToken(/*access_token=*/std::string(), /*with_error=*/true);
EXPECT_FALSE(IsAccessTokenRequestPending());
// Will not retry.
task_environment()->FastForwardBy(GetRefreshTokenDelay() * 1.1);
EXPECT_FALSE(IsAccessTokenRequestPending());
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest,
CheckAcquireAndReleaseWakeLockWhenBatteryIsCharging) {
// Simulate a device being connected to a charger initially.
SetPowerStateCharging();
// Lock screen to start ambient mode, and flush the loop to ensure
// the acquire wake lock request has reached the wake lock provider.
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
HideAmbientScreen();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Ambient screen showup again after inactivity.
FastForwardToLockScreenTimeout();
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Unlock screen to exit ambient mode.
UnlockScreen();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
}
TEST_F(AmbientControllerTest,
CheckAcquireAndReleaseWakeLockWhenBatteryBatteryIsFullAndDischarging) {
SetPowerStateDischarging();
SetBatteryPercent(100.f);
SetExternalPowerConnected();
// Lock screen to start ambient mode, and flush the loop to ensure
// the acquire wake lock request has reached the wake lock provider.
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
HideAmbientScreen();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Ambient screen showup again after inactivity.
FastForwardToLockScreenTimeout();
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Unlock screen to exit ambient mode.
UnlockScreen();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
}
TEST_F(AmbientControllerTest,
CheckAcquireAndReleaseWakeLockWhenBatteryStateChanged) {
SetPowerStateDischarging();
SetExternalPowerConnected();
SetBatteryPercent(50.f);
// Lock screen to start ambient mode.
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
// Should not acquire wake lock when device is not charging and with low
// battery.
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Connect the device with a charger.
SetPowerStateCharging();
base::RunLoop().RunUntilIdle();
// Should acquire the wake lock when battery is charging.
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Simulates a full battery.
SetBatteryPercent(100.f);
// Should keep the wake lock as the charger is still connected.
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Disconnects the charger again.
SetPowerStateDischarging();
base::RunLoop().RunUntilIdle();
// Should keep the wake lock when battery is high.
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
SetBatteryPercent(50.f);
base::RunLoop().RunUntilIdle();
// Should release the wake lock when battery is not charging and low.
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
SetBatteryPercent(100.f);
base::RunLoop().RunUntilIdle();
// Should take the wake lock when battery is not charging and high.
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
SetExternalPowerDisconnected();
base::RunLoop().RunUntilIdle();
// Should release the wake lock when power is not connected.
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// An unbalanced release should do nothing.
UnlockScreen();
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
}
// TODO(cowmoo): find a way to simulate events to trigger |UserActivityDetector|
TEST_F(AmbientControllerTest, ShouldDismissContainerViewOnEvents) {
std::vector<std::unique_ptr<ui::Event>> events;
for (auto mouse_event_type : {ui::ET_MOUSE_PRESSED, ui::ET_MOUSE_MOVED}) {
events.emplace_back(std::make_unique<ui::MouseEvent>(
mouse_event_type, gfx::Point(), gfx::Point(), base::TimeTicks(),
ui::EF_NONE, ui::EF_NONE));
}
events.emplace_back(std::make_unique<ui::MouseWheelEvent>(
gfx::Vector2d(), gfx::PointF(), gfx::PointF(), base::TimeTicks(),
ui::EF_NONE, ui::EF_NONE));
events.emplace_back(std::make_unique<ui::ScrollEvent>(
ui::ET_SCROLL, gfx::PointF(), gfx::PointF(), base::TimeTicks(),
ui::EF_NONE, /*x_offset=*/0.0f,
/*y_offset=*/0.0f,
/*x_offset_ordinal=*/0.0f,
/*x_offset_ordinal=*/0.0f, /*finger_count=*/2));
events.emplace_back(std::make_unique<ui::TouchEvent>(
ui::ET_TOUCH_PRESSED, gfx::PointF(), gfx::PointF(), base::TimeTicks(),
ui::PointerDetails()));
for (const auto& event : events) {
ShowAmbientScreen();
FastForwardTiny();
EXPECT_TRUE(WidgetsVisible());
ambient_controller()->OnUserActivity(event.get());
FastForwardTiny();
EXPECT_TRUE(GetContainerViews().empty());
// Clean up.
CloseAmbientScreen();
}
}
TEST_F(AmbientControllerTest, ShouldDismissAndThenComesBack) {
LockScreen();
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_TRUE(WidgetsVisible());
ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
base::TimeTicks(), ui::EF_NONE, ui::EF_NONE);
ambient_controller()->OnUserActivity(&mouse_event);
FastForwardTiny();
EXPECT_TRUE(GetContainerViews().empty());
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_TRUE(WidgetsVisible());
}
TEST_F(AmbientControllerTest, ShouldDismissContainerViewOnKeyEvent) {
// Without user interaction, should show ambient mode.
ambient_controller()->ShowUi();
EXPECT_FALSE(WidgetsVisible());
FastForwardTiny();
EXPECT_TRUE(WidgetsVisible());
CloseAmbientScreen();
// When ambient is shown, OnUserActivity() should ignore key event.
ambient_controller()->ShowUi();
EXPECT_TRUE(ambient_controller()->IsShown());
// General key press will exit ambient mode.
// Simulate key press to close the widget.
ui::test::EventGenerator* event_generator = GetEventGenerator();
event_generator->PressKey(ui::VKEY_A, /*flags=*/0);
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest,
ShouldDismissContainerViewOnKeyEventWhenLockScreenInBackground) {
GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
SetPowerStateCharging();
EXPECT_FALSE(ambient_controller()->IsShown());
// Should not lock the device and enter ambient mode when the screen is
// dimmed.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
EXPECT_FALSE(IsLocked());
EXPECT_TRUE(ambient_controller()->IsShown());
FastForwardToBackgroundLockScreenTimeout();
EXPECT_TRUE(IsLocked());
// Should not disrupt ongoing ambient mode.
EXPECT_TRUE(ambient_controller()->IsShown());
// General key press will exit ambient mode.
// Simulate key press to close the widget.
ui::test::EventGenerator* event_generator = GetEventGenerator();
event_generator->PressKey(ui::VKEY_A, /*flags=*/0);
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest,
ShouldShowAmbientScreenWithLockscreenWhenScreenIsDimmed) {
GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
SetPowerStateCharging();
EXPECT_FALSE(ambient_controller()->IsShown());
// Should enter ambient mode when the screen is dimmed.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
EXPECT_FALSE(IsLocked());
EXPECT_TRUE(ambient_controller()->IsShown());
FastForwardToBackgroundLockScreenTimeout();
EXPECT_TRUE(IsLocked());
// Should not disrupt ongoing ambient mode.
EXPECT_TRUE(ambient_controller()->IsShown());
// Closes ambient for clean-up.
UnlockScreen();
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest,
ShouldShowAmbientScreenWithLockscreenWithNoisyPowerEvents) {
GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
SetPowerStateCharging();
EXPECT_FALSE(ambient_controller()->IsShown());
// Should enter ambient mode when the screen is dimmed.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
EXPECT_FALSE(IsLocked());
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
FastForwardHalfLockScreenDelay();
SetPowerStateCharging();
FastForwardHalfLockScreenDelay();
SetPowerStateCharging();
EXPECT_TRUE(IsLocked());
// Should not disrupt ongoing ambient mode.
EXPECT_TRUE(ambient_controller()->IsShown());
// Closes ambient for clean-up.
UnlockScreen();
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest,
ShouldShowAmbientScreenWithoutLockscreenWhenScreenIsDimmed) {
GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
// When power is discharging, we do not lock the screen with ambient
// mode since we do not prevent the device go to sleep which will natually
// lock the device.
SetPowerStateDischarging();
EXPECT_FALSE(ambient_controller()->IsShown());
// Should not lock the device but still enter ambient mode when the screen is
// dimmed.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
EXPECT_FALSE(IsLocked());
EXPECT_TRUE(ambient_controller()->IsShown());
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
FastForwardToBackgroundLockScreenTimeout();
EXPECT_FALSE(IsLocked());
// Closes ambient for clean-up.
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, ShouldShowAmbientScreenWhenScreenIsDimmed) {
GetSessionControllerClient()->SetShouldLockScreenAutomatically(false);
SetPowerStateCharging();
EXPECT_FALSE(ambient_controller()->IsShown());
// Should not lock the device but enter ambient mode when the screen is
// dimmed.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
EXPECT_FALSE(IsLocked());
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
FastForwardToBackgroundLockScreenTimeout();
EXPECT_FALSE(IsLocked());
// Closes ambient for clean-up.
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, ShouldHideAmbientScreenWhenDisplayIsOff) {
GetSessionControllerClient()->SetShouldLockScreenAutomatically(false);
EXPECT_FALSE(ambient_controller()->IsShown());
// Should not lock the device and enter ambient mode when the screen is
// dimmed.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
EXPECT_FALSE(IsLocked());
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
// Should dismiss ambient mode screen.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/true);
FastForwardTiny();
EXPECT_FALSE(ambient_controller()->IsShown());
// Screen back on again, should not have ambient screen.
SetScreenIdleStateAndWait(/*dimmed=*/false, /*off=*/false);
FastForwardTiny();
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest,
ShouldHideAmbientScreenWhenDisplayIsOffThenComesBackWithLockScreen) {
GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
SetPowerStateCharging();
EXPECT_FALSE(ambient_controller()->IsShown());
// Should not lock the device and enter ambient mode when the screen is
// dimmed.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
EXPECT_FALSE(IsLocked());
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
FastForwardToBackgroundLockScreenTimeout();
EXPECT_TRUE(IsLocked());
// Should dismiss ambient mode screen.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/true);
FastForwardTiny();
EXPECT_FALSE(ambient_controller()->IsShown());
// Screen back on again, should not have ambient screen, but still has lock
// screen.
SetScreenIdleStateAndWait(/*dimmed=*/false, /*off=*/false);
EXPECT_TRUE(IsLocked());
EXPECT_FALSE(ambient_controller()->IsShown());
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest,
ShouldHideAmbientScreenWhenDisplayIsOffAndNotStartWhenLockScreen) {
GetSessionControllerClient()->SetShouldLockScreenAutomatically(true);
SetPowerStateDischarging();
EXPECT_FALSE(ambient_controller()->IsShown());
// Should not lock the device and enter ambient mode when the screen is
// dimmed.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/false);
EXPECT_FALSE(IsLocked());
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
// Should not lock the device because the device is not charging.
FastForwardToBackgroundLockScreenTimeout();
EXPECT_FALSE(IsLocked());
// Should dismiss ambient mode screen.
SetScreenIdleStateAndWait(/*dimmed=*/true, /*off=*/true);
FastForwardTiny();
EXPECT_FALSE(ambient_controller()->IsShown());
// Lock screen will not start ambient mode.
LockScreen();
EXPECT_TRUE(IsLocked());
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_FALSE(ambient_controller()->IsShown());
// Screen back on again, should not have ambient screen, but still has lock
// screen.
SetScreenIdleStateAndWait(/*dimmed=*/false, /*off=*/false);
EXPECT_TRUE(IsLocked());
EXPECT_FALSE(ambient_controller()->IsShown());
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_TRUE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest, HideCursor) {
auto* cursor_manager = Shell::Get()->cursor_manager();
LockScreen();
cursor_manager->ShowCursor();
EXPECT_TRUE(cursor_manager->IsCursorVisible());
FastForwardToLockScreenTimeout();
FastForwardTiny();
EXPECT_FALSE(GetContainerViews().empty());
EXPECT_EQ(AmbientUiModel::Get()->ui_visibility(),
AmbientUiVisibility::kShown);
EXPECT_TRUE(ambient_controller()->IsShown());
EXPECT_FALSE(cursor_manager->IsCursorVisible());
// Clean up.
UnlockScreen();
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest, ShowsOnMultipleDisplays) {
UpdateDisplay("800x600,800x600");
FastForwardTiny();
ShowAmbientScreen();
FastForwardToNextImage();
auto* screen = display::Screen::GetScreen();
EXPECT_EQ(screen->GetNumDisplays(), 2);
EXPECT_EQ(GetContainerViews().size(), 2u);
// Check that each root controller has an ambient widget.
for (auto* ctrl : RootWindowController::root_window_controllers())
EXPECT_TRUE(ctrl->ambient_widget_for_testing() &&
ctrl->ambient_widget_for_testing()->IsVisible());
}
TEST_F(AmbientControllerTest, RespondsToDisplayAdded) {
// UpdateDisplay triggers a rogue MouseEvent that cancels Ambient mode when
// testing with Xvfb. A corresponding MouseEvent is not fired on a real device
// when an external display is added. Ignore this MouseEvent for testing.
// Store the old |ShouldIgnoreNativePlatformEvents| value and reset it at the
// end of the test.
bool old_should_ignore_events =
ui::PlatformEventSource::ShouldIgnoreNativePlatformEvents();
ui::PlatformEventSource::SetIgnoreNativePlatformEvents(true);
UpdateDisplay("800x600");
ShowAmbientScreen();
FastForwardToNextImage();
auto* screen = display::Screen::GetScreen();
EXPECT_EQ(screen->GetNumDisplays(), 1);
EXPECT_EQ(GetContainerViews().size(), 1u);
UpdateDisplay("800x600,800x600");
FastForwardTiny();
EXPECT_TRUE(WidgetsVisible());
EXPECT_EQ(screen->GetNumDisplays(), 2);
EXPECT_EQ(GetContainerViews().size(), 2u);
for (auto* ctrl : RootWindowController::root_window_controllers())
EXPECT_TRUE(ctrl->ambient_widget_for_testing() &&
ctrl->ambient_widget_for_testing()->IsVisible());
ui::PlatformEventSource::SetIgnoreNativePlatformEvents(
old_should_ignore_events);
}
TEST_F(AmbientControllerTest, HandlesDisplayRemoved) {
UpdateDisplay("800x600,800x600");
FastForwardTiny();
ShowAmbientScreen();
FastForwardToNextImage();
auto* screen = display::Screen::GetScreen();
EXPECT_EQ(screen->GetNumDisplays(), 2);
EXPECT_EQ(GetContainerViews().size(), 2u);
EXPECT_TRUE(WidgetsVisible());
// Changing to one screen will destroy the widget on the non-primary screen.
UpdateDisplay("800x600");
FastForwardTiny();
EXPECT_EQ(screen->GetNumDisplays(), 1);
EXPECT_EQ(GetContainerViews().size(), 1u);
EXPECT_TRUE(WidgetsVisible());
}
TEST_F(AmbientControllerTest, ClosesAmbientBeforeSuspend) {
LockScreen();
FastForwardToLockScreenTimeout();
EXPECT_TRUE(ambient_controller()->IsShown());
SimulateSystemSuspendAndWait(power_manager::SuspendImminent::Reason::
SuspendImminent_Reason_LID_CLOSED);
EXPECT_FALSE(ambient_controller()->IsShown());
FastForwardToLockScreenTimeout();
// Ambient mode should not resume until SuspendDone is received.
EXPECT_FALSE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest, RestartsAmbientAfterSuspend) {
LockScreen();
FastForwardToLockScreenTimeout();
EXPECT_TRUE(ambient_controller()->IsShown());
SimulateSystemSuspendAndWait(
power_manager::SuspendImminent::Reason::SuspendImminent_Reason_IDLE);
EXPECT_FALSE(ambient_controller()->IsShown());
// This call should be blocked by prior |SuspendImminent| until |SuspendDone|.
ambient_controller()->ShowUi();
EXPECT_FALSE(ambient_controller()->IsShown());
SimulateSystemResumeAndWait();
FastForwardToLockScreenTimeout();
EXPECT_TRUE(ambient_controller()->IsShown());
}
TEST_F(AmbientControllerTest, ObservesPrefsWhenAmbientEnabled) {
SetAmbientModeEnabled(false);
// This pref is always observed.
EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));
std::vector<std::string> other_prefs{
ambient::prefs::kAmbientModeLockScreenInactivityTimeoutSeconds,
ambient::prefs::kAmbientModeLockScreenBackgroundTimeoutSeconds,
ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds};
for (auto& pref_name : other_prefs)
EXPECT_FALSE(IsPrefObserved(pref_name));
SetAmbientModeEnabled(true);
EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));
for (auto& pref_name : other_prefs)
EXPECT_TRUE(IsPrefObserved(pref_name));
}
TEST_F(AmbientControllerTest, BindsObserversWhenAmbientEnabled) {
auto* ctrl = ambient_controller();
SetAmbientModeEnabled(false);
// SessionObserver must always be observing to detect when user pref service
// is started.
EXPECT_TRUE(ctrl->session_observer_.IsObserving());
EXPECT_FALSE(AreSessionSpecificObserversBound());
SetAmbientModeEnabled(true);
// Session observer should still be observing.
EXPECT_TRUE(ctrl->session_observer_.IsObserving());
EXPECT_TRUE(AreSessionSpecificObserversBound());
}
TEST_F(AmbientControllerTest, SwitchActiveUsersDoesNotDoubleBindObservers) {
ClearLogin();
SimulateUserLogin(kUser1);
SetAmbientModeEnabled(true);
TestSessionControllerClient* session = GetSessionControllerClient();
// Observers are bound for primary user with Ambient mode enabled.
EXPECT_TRUE(AreSessionSpecificObserversBound());
EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));
// Observers are still bound when secondary user logs in.
SimulateUserLogin(kUser2);
EXPECT_TRUE(AreSessionSpecificObserversBound());
EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));
// Observers are not re-bound for primary user when session is active.
session->SwitchActiveUser(AccountId::FromUserEmail(kUser1));
EXPECT_TRUE(AreSessionSpecificObserversBound());
EXPECT_TRUE(IsPrefObserved(ambient::prefs::kAmbientModeEnabled));
// Switch back to secondary user.
session->SwitchActiveUser(AccountId::FromUserEmail(kUser2));
}
TEST_F(AmbientControllerTest, BindsObserversWhenAmbientOn) {
auto* ctrl = ambient_controller();
LockScreen();
// Start monitoring user activity on hidden ui.
EXPECT_TRUE(ctrl->user_activity_observer_.IsObserving());
// Do not monitor power status yet.
EXPECT_FALSE(ctrl->power_status_observer_.IsObserving());
FastForwardToLockScreenTimeout();
EXPECT_TRUE(ctrl->user_activity_observer_.IsObserving());
EXPECT_TRUE(ctrl->power_status_observer_.IsObserving());
UnlockScreen();
EXPECT_FALSE(ctrl->user_activity_observer_.IsObserving());
EXPECT_FALSE(ctrl->power_status_observer_.IsObserving());
}
TEST_F(AmbientControllerTest, ShowDismissAmbientScreenUponAssistantQuery) {
// Without user interaction, should show ambient mode.
ShowAmbientScreen();
EXPECT_TRUE(ambient_controller()->IsShown());
// Trigger Assistant interaction.
static_cast<AssistantInteractionControllerImpl*>(
AssistantInteractionController::Get())
->OnInteractionStarted(AssistantInteractionMetadata());
base::RunLoop().RunUntilIdle();
// Ambient screen should dismiss.
EXPECT_TRUE(GetContainerViews().empty());
EXPECT_FALSE(ambient_controller()->IsShown());
}
} // namespace ash