Commit 8c521423 authored by Meilin Wang's avatar Meilin Wang Committed by Commit Bot

ambient: handle system suspend/resume.

This CL handles the system suspend/resume events accordingly when
ambient is on.

Misc:
Minor refactoring including: rename functions and use
|ScopedObserver| instead.

Bug: b/157252087
Test: covered by unittests.
Change-Id: I3a1244e2aa5e47fd83082a7ada40e5b2fb5aabd4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2270676
Commit-Queue: Meilin Wang <meilinw@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#785983}
parent 66b95df6
......@@ -996,7 +996,7 @@ bool CanHandleStartAmbientMode() {
}
void HandleToggleAmbientMode(const ui::Accelerator& accelerator) {
Shell::Get()->ambient_controller()->ToggleInSessionUI();
Shell::Get()->ambient_controller()->ToggleInSessionUi();
}
void HandleToggleAssistant(const ui::Accelerator& accelerator) {
......
......@@ -23,6 +23,7 @@
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/power/power_status.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
......@@ -30,6 +31,7 @@
#include "build/buildflag.h"
#include "chromeos/assistant/buildflags.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/services/assistant/public/cpp/assistant_service.h"
#include "components/prefs/pref_registry_simple.h"
#include "ui/base/ui_base_types.h"
......@@ -87,6 +89,26 @@ bool IsChargerConnected() {
PowerStatus::Get()->IsLinePowerConnected();
}
bool IsUiShown(AmbientUiVisibility visibility) {
return visibility == AmbientUiVisibility::kShown;
}
bool IsUiHidden(AmbientUiVisibility visibility) {
return visibility == AmbientUiVisibility::kHidden;
}
bool IsUiClosed(AmbientUiVisibility visibility) {
return visibility == AmbientUiVisibility::kClosed;
}
bool IsLockScreenUi(AmbientUiMode mode) {
return mode == AmbientUiMode::kLockScreenUi;
}
bool IsInSessionUi(AmbientUiMode mode) {
return mode == AmbientUiMode::kInSessionUi;
}
} // namespace
// AmbientController::InactivityMonitor----------------------------------
......@@ -143,18 +165,23 @@ void AmbientController::RegisterProfilePrefs(PrefRegistrySimple* registry) {
AmbientController::AmbientController() {
ambient_backend_controller_ = CreateAmbientBackendController();
ambient_ui_model_.AddObserver(this);
ambient_ui_model_observer_.Add(&ambient_ui_model_);
// |SessionController| is initialized before |this| in Shell.
Shell::Get()->session_controller()->AddObserver(this);
session_observer_.Add(Shell::Get()->session_controller());
// Checks the current lid state on initialization.
auto* power_manager_client = chromeos::PowerManagerClient::Get();
DCHECK(power_manager_client);
power_manager_client_observer_.Add(power_manager_client);
power_manager_client->RequestStatusUpdate();
power_manager_client->GetSwitchStates(
base::BindOnce(&AmbientController::OnReceiveSwitchStates,
weak_ptr_factory_.GetWeakPtr()));
}
AmbientController::~AmbientController() {
if (container_view_)
container_view_->GetWidget()->CloseNow();
// |SessionController| is destroyed after |this| in Shell.
Shell::Get()->session_controller()->RemoveObserver(this);
ambient_ui_model_.RemoveObserver(this);
}
void AmbientController::OnAmbientUiVisibilityChanged(
......@@ -196,7 +223,7 @@ void AmbientController::OnAmbientUiVisibilityChanged(
// Creates the monitor and starts the auto-show timer upon hidden.
DCHECK(!inactivity_monitor_);
if (LockScreen::HasInstance()) {
if (LockScreen::HasInstance() && autoshow_enabled_) {
inactivity_monitor_ = std::make_unique<InactivityMonitor>(
LockScreen::Get()->widget()->GetWeakPtr(),
base::BindOnce(&AmbientController::OnAutoShowTimeOut,
......@@ -222,7 +249,7 @@ void AmbientController::OnAmbientUiVisibilityChanged(
}
void AmbientController::OnAutoShowTimeOut() {
DCHECK_EQ(ambient_ui_model_.ui_visibility(), AmbientUiVisibility::kHidden);
DCHECK(IsUiHidden(ambient_ui_model_.ui_visibility()));
DCHECK(!container_view_->GetWidget()->IsVisible());
// Show ambient screen after time out.
......@@ -230,8 +257,10 @@ void AmbientController::OnAutoShowTimeOut() {
}
void AmbientController::OnLockStateChanged(bool locked) {
if (!AmbientClient::Get()->IsAmbientModeAllowed())
if (!AmbientClient::Get()->IsAmbientModeAllowed()) {
VLOG(1) << "Ambient mode is not allowed.";
return;
}
if (locked) {
// We have 3 options to manage the token for lock screen. Here use option 1.
......@@ -253,15 +282,50 @@ void AmbientController::OnLockStateChanged(bool locked) {
// has already been fetched, then there is not additional time to
// wait.
RequestAccessToken(base::DoNothing());
ambient_ui_model_.SetUiMode(AmbientUiMode::kLockScreenUi);
ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kShown);
ShowUi(AmbientUiMode::kLockScreenUi);
} else {
DCHECK(container_view_);
// Ambient screen will be destroyed along with the lock screen when user
// logs in.
ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kClosed);
CloseUi();
}
}
void AmbientController::LidEventReceived(
chromeos::PowerManagerClient::LidState state,
const base::TimeTicks& timestamp) {
// We still need to observe the lid closed event despite of suspend signals
// to release the wake lock acquired if any.
bool lid_closed = (state != chromeos::PowerManagerClient::LidState::OPEN);
if (lid_closed)
ReleaseWakeLock();
}
void AmbientController::SuspendImminent(
power_manager::SuspendImminent::Reason reason) {
// This is triggered when the system is about to suspend and the display will
// be turned off, we should handle this event by dismissing ambient (if not
// yet) and cancel the auto-show timer.
if (IsUiClosed(ambient_ui_model_.ui_visibility())) {
// No action needed if ambient screen has closed.
return;
}
HandleOnSuspend();
}
void AmbientController::SuspendDone(const base::TimeDelta& sleep_duration) {
// This is triggered when the system resumes after an earlier suspension, we
// should handle this event by restarting the auto-show timer if necessary.
if (IsUiClosed(ambient_ui_model_.ui_visibility())) {
// No action needed if ambient screen has closed.
return;
}
HandleOnResume();
}
void AmbientController::OnPowerStatusChanged() {
if (ambient_ui_model_.ui_visibility() != AmbientUiVisibility::kShown) {
// No action needed if ambient screen is not shown.
......@@ -293,13 +357,7 @@ std::unique_ptr<AmbientContainerView> AmbientController::CreateContainerView() {
return container;
}
void AmbientController::HideContainerView() {
DCHECK(container_view_);
ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kHidden);
}
void AmbientController::ShowInSessionUI() {
void AmbientController::ShowUi(AmbientUiMode mode) {
// TODO(meilinw): move the eligibility check to the idle entry point once
// implemented: b/149246117.
if (!AmbientClient::Get()->IsAmbientModeAllowed()) {
......@@ -307,21 +365,28 @@ void AmbientController::ShowInSessionUI() {
return;
}
ambient_ui_model_.SetUiMode(AmbientUiMode::kInSessionUi);
ambient_ui_model_.SetUiMode(mode);
ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kShown);
}
void AmbientController::CloseInSessionUI() {
void AmbientController::CloseUi() {
DCHECK(container_view_);
ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kClosed);
}
void AmbientController::ToggleInSessionUI() {
void AmbientController::HideLockScreenUi() {
DCHECK(container_view_);
DCHECK(IsLockScreenUi(ambient_ui_model_.ui_mode()));
ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kHidden);
}
void AmbientController::ToggleInSessionUi() {
if (!container_view_)
ShowInSessionUI();
ShowUi(AmbientUiMode::kInSessionUi);
else
CloseInSessionUI();
CloseUi();
}
bool AmbientController::IsShown() const {
......@@ -330,10 +395,10 @@ bool AmbientController::IsShown() const {
void AmbientController::OnBackgroundPhotoEvents() {
// Dismisses the ambient screen when user interacts with the background photo.
if (ambient_ui_model_.ui_mode() == AmbientUiMode::kInSessionUi)
CloseInSessionUI();
if (IsLockScreenUi(ambient_ui_model_.ui_mode()))
HideLockScreenUi();
else
HideContainerView();
CloseUi();
}
void AmbientController::UpdateUiMode(AmbientUiMode ui_mode) {
......@@ -364,6 +429,43 @@ void AmbientController::ReleaseWakeLock() {
VLOG(1) << "Released wake lock";
}
void AmbientController::OnReceiveSwitchStates(
base::Optional<chromeos::PowerManagerClient::SwitchStates> switch_states) {
autoshow_enabled_ = (switch_states->lid_state !=
chromeos::PowerManagerClient::LidState::CLOSED);
}
void AmbientController::HandleOnSuspend() {
// Disables auto-show and reset the timer if created.
autoshow_enabled_ = false;
inactivity_monitor_.reset();
// Has no effect if no wake lock is acquired.
ReleaseWakeLock();
// Dismiss ambient screen.
if (IsLockScreenUi(ambient_ui_model_.ui_mode())) {
// No-op if UI has already been hidden.
HideLockScreenUi();
} else {
DCHECK(IsInSessionUi(ambient_ui_model_.ui_mode()));
CloseUi();
}
}
void AmbientController::HandleOnResume() {
DCHECK(!IsUiShown(ambient_ui_model_.ui_visibility()));
// Enables auto-show and starts the timer.
autoshow_enabled_ = true;
if (LockScreen::HasInstance()) {
inactivity_monitor_ = std::make_unique<InactivityMonitor>(
LockScreen::Get()->widget()->GetWeakPtr(),
base::BindOnce(&AmbientController::OnAutoShowTimeOut,
weak_ptr_factory_.GetWeakPtr()));
}
}
void AmbientController::RequestAccessToken(
AmbientAccessTokenController::AccessTokenCallback callback) {
access_token_controller_.RequestAccessToken(std::move(callback));
......
......@@ -15,10 +15,12 @@
#include "ash/ash_export.h"
#include "ash/public/cpp/ambient/ambient_ui_model.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/session/session_controller_impl.h"
#include "ash/system/power/power_status.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/device/public/mojom/wake_lock.mojom.h"
#include "ui/views/widget/widget.h"
......@@ -34,9 +36,11 @@ class AmbientPhotoController;
class AmbientViewDelegateObserver;
// Class to handle all ambient mode functionalities.
class ASH_EXPORT AmbientController : public AmbientUiModelObserver,
public SessionObserver,
public PowerStatus::Observer {
class ASH_EXPORT AmbientController
: public AmbientUiModelObserver,
public SessionObserver,
public PowerStatus::Observer,
public chromeos::PowerManagerClient::Observer {
public:
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
......@@ -52,21 +56,26 @@ class ASH_EXPORT AmbientController : public AmbientUiModelObserver,
// PowerStatus::Observer:
void OnPowerStatusChanged() override;
// chromeos::PowerManagerClient::Observer:
void LidEventReceived(chromeos::PowerManagerClient::LidState state,
const base::TimeTicks& timestamp) override;
void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
void SuspendDone(const base::TimeDelta& sleep_duration) override;
void AddAmbientViewDelegateObserver(AmbientViewDelegateObserver* observer);
void RemoveAmbientViewDelegateObserver(AmbientViewDelegateObserver* observer);
// Initializes the |container_view_|. Called in |CreateWidget()| as the
// Initializes the |container_view_|. Called in |CreateWidget()| to create the
// contents view.
std::unique_ptr<AmbientContainerView> CreateContainerView();
// Hides the |container_view_|. Called in |OnBackgroundPhotoEvents()| when
// user interacts with the lock-screen UI.
void HideContainerView();
// Invoked to show/close ambient UI in |mode|.
void ShowUi(AmbientUiMode mode);
void CloseUi();
void HideLockScreenUi();
// Handles the in-session ambient UI, which is displayed in its own widget.
void ShowInSessionUI();
void CloseInSessionUI();
void ToggleInSessionUI();
void ToggleInSessionUi();
// Returns true if the |container_view_| is currently visible.
bool IsShown() const;
......@@ -115,6 +124,16 @@ class ASH_EXPORT AmbientController : public AmbientUiModelObserver,
// Release |wake_lock_|. Unbalanced release call will have no effect.
void ReleaseWakeLock();
// Updates |autoshow_enabled_| flag based on the lid state.
void OnReceiveSwitchStates(
base::Optional<chromeos::PowerManagerClient::SwitchStates> switch_states);
// Invoked to dismiss ambient when the device is suspending.
void HandleOnSuspend();
// Invoked to restart the auto-show timer when the device is resuming.
void HandleOnResume();
AmbientPhotoController* get_ambient_photo_controller_for_testing() {
return &ambient_photo_controller_;
}
......@@ -139,8 +158,19 @@ class ASH_EXPORT AmbientController : public AmbientUiModelObserver,
// Lazily initialized on the first call of |AcquireWakeLock|.
mojo::Remote<device::mojom::WakeLock> wake_lock_;
ScopedObserver<AmbientUiModel, AmbientUiModelObserver>
ambient_ui_model_observer_{this};
ScopedObserver<SessionControllerImpl, SessionObserver> session_observer_{
this};
ScopedObserver<PowerStatus, PowerStatus::Observer> power_status_observer_{
this};
ScopedObserver<chromeos::PowerManagerClient,
chromeos::PowerManagerClient::Observer>
power_manager_client_observer_{this};
// Whether ambient screen will be brought up from hidden after long device
// inactivity.
bool autoshow_enabled_ = false;
base::WeakPtrFactory<AmbientController> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(AmbientController);
......
......@@ -15,6 +15,8 @@
#include "base/run_loop.h"
#include "base/test/bind_test_util.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 "ui/events/keycodes/keyboard_codes_posix.h"
......@@ -259,4 +261,42 @@ TEST_F(AmbientControllerTest, ShouldDismissContainerViewWhenKeyPressed) {
CloseAmbientScreen();
}
TEST_F(AmbientControllerTest, UpdateUiAndWakeLockWhenSystemSuspendOrResume) {
// Flush the loop first to ensure the |PowerStatus| has picked up the initial
// status.
base::RunLoop().RunUntilIdle();
// Simulate a device being connected with a charger initially to acquire a
// wake lock upon shown.
power_manager::PowerSupplyProperties proto;
proto.set_battery_state(
power_manager::PowerSupplyProperties_BatteryState_CHARGING);
PowerStatus::Get()->SetProtoForTesting(proto);
// Starts ambient screen.
ShowAmbientScreen();
EXPECT_TRUE(ambient_controller()->IsShown());
EXPECT_EQ(1, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Simulates the system starting to suspend by lid closed.
SimulateSystemSuspendAndWait(
power_manager::SuspendImminent_Reason_LID_CLOSED);
// System suspension should hide Ui and release the wake lock acquired
// previously.
EXPECT_FALSE(container_view()->GetWidget()->IsVisible());
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
// Simulates the system starting to resume.
SimulateSystemResumeAndWait();
// System resume should not invoke Ui to show up, thus no wake lock needed
// to acquire.
EXPECT_FALSE(container_view()->GetWidget()->IsVisible());
EXPECT_EQ(0, GetNumOfActiveWakeLocks(
device::mojom::WakeLockType::kPreventDisplaySleep));
}
} // namespace ash
......@@ -18,6 +18,8 @@
#include "base/run_loop.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_unittest_util.h"
......@@ -67,6 +69,7 @@ void AmbientAshTestBase::SetUp() {
chromeos::features::kAmbientModeFeature);
image_downloader_ = std::make_unique<TestImageDownloader>();
ambient_client_ = std::make_unique<TestAmbientClient>(&wake_lock_provider_);
chromeos::PowerManagerClient::InitializeFake();
AshTestBase::SetUp();
......@@ -90,14 +93,13 @@ void AmbientAshTestBase::TearDown() {
void AmbientAshTestBase::ShowAmbientScreen() {
// The widget will be destroyed in |AshTestBase::TearDown()|.
ambient_controller()->ambient_ui_model()->SetUiVisibility(
AmbientUiVisibility::kShown);
ambient_controller()->ShowUi(AmbientUiMode::kLockScreenUi);
// Flush the message loop to finish all async calls.
base::RunLoop().RunUntilIdle();
}
void AmbientAshTestBase::HideAmbientScreen() {
ambient_controller()->HideContainerView();
ambient_controller()->HideLockScreenUi();
}
void AmbientAshTestBase::CloseAmbientScreen() {
......@@ -113,6 +115,17 @@ void AmbientAshTestBase::UnlockScreen() {
GetSessionControllerClient()->UnlockScreen();
}
void AmbientAshTestBase::SimulateSystemSuspendAndWait(
power_manager::SuspendImminent::Reason reason) {
chromeos::FakePowerManagerClient::Get()->SendSuspendImminent(reason);
base::RunLoop().RunUntilIdle();
}
void AmbientAshTestBase::SimulateSystemResumeAndWait() {
chromeos::FakePowerManagerClient::Get()->SendSuspendDone();
base::RunLoop().RunUntilIdle();
}
const gfx::ImageSkia& AmbientAshTestBase::GetImageInPhotoView() {
return container_view()
->photo_view_for_testing()
......
......@@ -51,6 +51,15 @@ class AmbientAshTestBase : public AshTestBase {
void LockScreen();
void UnlockScreen();
// Simulates the system starting to suspend with Reason |reason|.
// Wait until the event has been processed.
void SimulateSystemSuspendAndWait(
power_manager::SuspendImminent::Reason reason);
// Simulates the system starting to resume.
// Wait until the event has been processed.
void SimulateSystemResumeAndWait();
const gfx::ImageSkia& GetImageInPhotoView();
// Returns the number of active wake locks of type |type|.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment