| // Copyright 2016 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/session/session_controller_impl.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "ash/metrics/user_metrics_recorder.h" |
| #include "ash/public/cpp/session/session_activation_observer.h" |
| #include "ash/public/cpp/session/session_controller_client.h" |
| #include "ash/public/cpp/session/session_observer.h" |
| #include "ash/public/cpp/session/user_info.h" |
| #include "ash/session/fullscreen_controller.h" |
| #include "ash/session/multiprofiles_intro_dialog.h" |
| #include "ash/session/session_aborted_dialog.h" |
| #include "ash/session/teleport_warning_dialog.h" |
| #include "ash/shell.h" |
| #include "ash/system/power/power_event_observer.h" |
| #include "ash/system/screen_security/screen_switch_check_controller.h" |
| #include "ash/wm/lock_state_controller.h" |
| #include "ash/wm/mru_window_tracker.h" |
| #include "ash/wm/window_util.h" |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "components/account_id/account_id.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/user_type.h" |
| #include "ui/message_center/message_center.h" |
| |
| using session_manager::SessionState; |
| |
| namespace ash { |
| |
| SessionControllerImpl::SessionControllerImpl() |
| : fullscreen_controller_(std::make_unique<FullscreenController>(this)) {} |
| |
| SessionControllerImpl::~SessionControllerImpl() { |
| // Abort pending start lock request. |
| if (!start_lock_callback_.is_null()) |
| std::move(start_lock_callback_).Run(false /* locked */); |
| } |
| |
| int SessionControllerImpl::NumberOfLoggedInUsers() const { |
| return static_cast<int>(user_sessions_.size()); |
| } |
| |
| AccountId SessionControllerImpl::GetActiveAccountId() const { |
| return user_sessions_.empty() ? AccountId() |
| : user_sessions_[0]->user_info.account_id; |
| } |
| |
| AddUserSessionPolicy SessionControllerImpl::GetAddUserPolicy() const { |
| return add_user_session_policy_; |
| } |
| |
| bool SessionControllerImpl::IsActiveUserSessionStarted() const { |
| return !user_sessions_.empty(); |
| } |
| |
| bool SessionControllerImpl::CanLockScreen() const { |
| return IsActiveUserSessionStarted() && can_lock_; |
| } |
| |
| bool SessionControllerImpl::ShouldLockScreenAutomatically() const { |
| return should_lock_screen_automatically_; |
| } |
| |
| bool SessionControllerImpl::IsRunningInAppMode() const { |
| return is_running_in_app_mode_; |
| } |
| |
| bool SessionControllerImpl::IsDemoSession() const { |
| return is_demo_session_; |
| } |
| |
| bool SessionControllerImpl::IsUserSessionBlocked() const { |
| // User sessions are blocked when session state is not ACTIVE, with two |
| // exceptions: |
| // - LOGGED_IN_NOT_ACTIVE state. This is needed so that browser windows |
| // created by session restore (or a default new browser window) are properly |
| // activated before session state changes to ACTIVE. |
| // - LOCKED state with a running unlocking animation. This is needed because |
| // the unlocking animation hides the lock container at the end. During the |
| // unlock animation, IsUserSessionBlocked needs to return unblocked so that |
| // user windows are deemed activatable and ash correctly restores the active |
| // window before locking. |
| return state_ != SessionState::ACTIVE && |
| state_ != SessionState::LOGGED_IN_NOT_ACTIVE && |
| !(state_ == SessionState::LOCKED && is_unlocking_); |
| } |
| |
| bool SessionControllerImpl::IsInSecondaryLoginScreen() const { |
| return state_ == SessionState::LOGIN_SECONDARY; |
| } |
| |
| SessionState SessionControllerImpl::GetSessionState() const { |
| return state_; |
| } |
| |
| bool SessionControllerImpl::ShouldEnableSettings() const { |
| // Settings opens a web UI window, so it is not available at the lock screen. |
| if (!IsActiveUserSessionStarted() || IsScreenLocked() || |
| IsInSecondaryLoginScreen()) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool SessionControllerImpl::ShouldShowNotificationTray() const { |
| if (!IsActiveUserSessionStarted() || IsInSecondaryLoginScreen()) |
| return false; |
| |
| return true; |
| } |
| |
| const SessionControllerImpl::UserSessions& |
| SessionControllerImpl::GetUserSessions() const { |
| return user_sessions_; |
| } |
| |
| const UserSession* SessionControllerImpl::GetUserSession( |
| UserIndex index) const { |
| if (index < 0 || index >= static_cast<UserIndex>(user_sessions_.size())) |
| return nullptr; |
| |
| return user_sessions_[index].get(); |
| } |
| |
| const UserSession* SessionControllerImpl::GetUserSessionByAccountId( |
| const AccountId& account_id) const { |
| auto it = |
| std::find_if(user_sessions_.begin(), user_sessions_.end(), |
| [&account_id](const std::unique_ptr<UserSession>& session) { |
| return session->user_info.account_id == account_id; |
| }); |
| if (it == user_sessions_.end()) |
| return nullptr; |
| |
| return (*it).get(); |
| } |
| |
| const UserSession* SessionControllerImpl::GetPrimaryUserSession() const { |
| auto it = std::find_if(user_sessions_.begin(), user_sessions_.end(), |
| [this](const std::unique_ptr<UserSession>& session) { |
| return session->session_id == primary_session_id_; |
| }); |
| if (it == user_sessions_.end()) |
| return nullptr; |
| |
| return (*it).get(); |
| } |
| |
| bool SessionControllerImpl::IsUserSupervised() const { |
| if (!IsActiveUserSessionStarted()) |
| return false; |
| |
| user_manager::UserType active_user_type = GetUserSession(0)->user_info.type; |
| return active_user_type == user_manager::USER_TYPE_SUPERVISED || |
| active_user_type == user_manager::USER_TYPE_CHILD; |
| } |
| |
| bool SessionControllerImpl::IsUserLegacySupervised() const { |
| if (!IsActiveUserSessionStarted()) |
| return false; |
| |
| user_manager::UserType active_user_type = GetUserSession(0)->user_info.type; |
| return active_user_type == user_manager::USER_TYPE_SUPERVISED; |
| } |
| |
| bool SessionControllerImpl::IsUserChild() const { |
| if (!IsActiveUserSessionStarted()) |
| return false; |
| |
| user_manager::UserType active_user_type = GetUserSession(0)->user_info.type; |
| return active_user_type == user_manager::USER_TYPE_CHILD; |
| } |
| |
| bool SessionControllerImpl::IsUserPublicAccount() const { |
| if (!IsActiveUserSessionStarted()) |
| return false; |
| |
| user_manager::UserType active_user_type = GetUserSession(0)->user_info.type; |
| return active_user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT; |
| } |
| |
| base::Optional<user_manager::UserType> SessionControllerImpl::GetUserType() |
| const { |
| if (!IsActiveUserSessionStarted()) |
| return base::nullopt; |
| |
| return base::make_optional(GetUserSession(0)->user_info.type); |
| } |
| |
| bool SessionControllerImpl::IsUserPrimary() const { |
| if (!IsActiveUserSessionStarted()) |
| return false; |
| |
| return GetUserSession(0)->session_id == primary_session_id_; |
| } |
| |
| bool SessionControllerImpl::IsUserFirstLogin() const { |
| if (!IsActiveUserSessionStarted()) |
| return false; |
| |
| return GetUserSession(0)->user_info.is_new_profile; |
| } |
| |
| bool SessionControllerImpl::ShouldDisplayManagedUI() const { |
| if (!IsActiveUserSessionStarted()) |
| return false; |
| |
| return GetUserSession(0)->user_info.should_display_managed_ui; |
| } |
| |
| void SessionControllerImpl::LockScreen() { |
| if (client_) |
| client_->RequestLockScreen(); |
| } |
| |
| void SessionControllerImpl::RequestSignOut() { |
| if (client_) |
| client_->RequestSignOut(); |
| } |
| |
| void SessionControllerImpl::AttemptRestartChrome() { |
| if (client_) |
| client_->AttemptRestartChrome(); |
| } |
| |
| void SessionControllerImpl::SwitchActiveUser(const AccountId& account_id) { |
| if (client_) |
| client_->SwitchActiveUser(account_id); |
| } |
| |
| void SessionControllerImpl::CycleActiveUser(CycleUserDirection direction) { |
| if (client_) |
| client_->CycleActiveUser(direction); |
| } |
| |
| void SessionControllerImpl::ShowMultiProfileLogin() { |
| if (client_) |
| client_->ShowMultiProfileLogin(); |
| } |
| |
| void SessionControllerImpl::EmitAshInitialized() { |
| if (client_) |
| client_->EmitAshInitialized(); |
| } |
| |
| PrefService* SessionControllerImpl::GetSigninScreenPrefService() const { |
| return client_ ? client_->GetSigninScreenPrefService() : nullptr; |
| } |
| |
| PrefService* SessionControllerImpl::GetUserPrefServiceForUser( |
| const AccountId& account_id) const { |
| return client_ ? client_->GetUserPrefService(account_id) : nullptr; |
| } |
| |
| PrefService* SessionControllerImpl::GetPrimaryUserPrefService() const { |
| const UserSession* session = GetPrimaryUserSession(); |
| return session ? GetUserPrefServiceForUser(session->user_info.account_id) |
| : nullptr; |
| } |
| |
| PrefService* SessionControllerImpl::GetLastActiveUserPrefService() const { |
| return last_active_user_prefs_; |
| } |
| |
| PrefService* SessionControllerImpl::GetActivePrefService() const { |
| // Use the active user prefs once they become available. Check the PrefService |
| // object instead of session state because prefs load is async after login. |
| if (last_active_user_prefs_) |
| return last_active_user_prefs_; |
| return GetSigninScreenPrefService(); |
| } |
| |
| void SessionControllerImpl::AddObserver(SessionObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void SessionControllerImpl::RemoveObserver(SessionObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| bool SessionControllerImpl::IsScreenLocked() const { |
| return state_ == SessionState::LOCKED; |
| } |
| |
| void SessionControllerImpl::SetClient(SessionControllerClient* client) { |
| client_ = client; |
| } |
| |
| void SessionControllerImpl::SetSessionInfo(const SessionInfo& info) { |
| can_lock_ = info.can_lock_screen; |
| should_lock_screen_automatically_ = info.should_lock_screen_automatically; |
| is_running_in_app_mode_ = info.is_running_in_app_mode; |
| if (info.is_demo_session) |
| SetIsDemoSession(); |
| add_user_session_policy_ = info.add_user_session_policy; |
| SetSessionState(info.state); |
| } |
| |
| void SessionControllerImpl::UpdateUserSession(const UserSession& user_session) { |
| auto it = std::find_if( |
| user_sessions_.begin(), user_sessions_.end(), |
| [&user_session](const std::unique_ptr<UserSession>& session) { |
| return session->session_id == user_session.session_id; |
| }); |
| if (it == user_sessions_.end()) { |
| AddUserSession(user_session); |
| return; |
| } |
| |
| *it = std::make_unique<UserSession>(user_session); |
| for (auto& observer : observers_) |
| observer.OnUserSessionUpdated((*it)->user_info.account_id); |
| |
| UpdateLoginStatus(); |
| } |
| |
| void SessionControllerImpl::SetUserSessionOrder( |
| const std::vector<uint32_t>& user_session_order) { |
| DCHECK_EQ(user_sessions_.size(), user_session_order.size()); |
| |
| AccountId last_active_account_id; |
| if (user_sessions_.size()) |
| last_active_account_id = user_sessions_[0]->user_info.account_id; |
| |
| // Adjusts |user_sessions_| to match the given order. |
| std::vector<std::unique_ptr<UserSession>> sessions; |
| for (const auto& session_id : user_session_order) { |
| auto it = |
| std::find_if(user_sessions_.begin(), user_sessions_.end(), |
| [session_id](const std::unique_ptr<UserSession>& session) { |
| return session && session->session_id == session_id; |
| }); |
| if (it == user_sessions_.end()) { |
| LOG(ERROR) << "Unknown session id =" << session_id; |
| continue; |
| } |
| |
| sessions.push_back(std::move(*it)); |
| } |
| |
| user_sessions_.swap(sessions); |
| |
| // Check active user change and notifies observers. |
| if (user_sessions_[0]->session_id != active_session_id_) { |
| const bool is_first_session = active_session_id_ == 0u; |
| active_session_id_ = user_sessions_[0]->session_id; |
| |
| if (is_first_session) { |
| for (auto& observer : observers_) |
| observer.OnFirstSessionStarted(); |
| } |
| |
| session_activation_observer_holder_.NotifyActiveSessionChanged( |
| last_active_account_id, user_sessions_[0]->user_info.account_id); |
| |
| // When switching to a user for whose PrefService is not ready, |
| // |last_active_user_prefs_| continues to point to the PrefService of the |
| // most-recently active user with a loaded PrefService. |
| PrefService* user_pref_service = |
| GetUserPrefServiceForUser(user_sessions_[0]->user_info.account_id); |
| if (user_pref_service && last_active_user_prefs_ != user_pref_service) { |
| last_active_user_prefs_ = user_pref_service; |
| MaybeNotifyOnActiveUserPrefServiceChanged(); |
| } |
| |
| for (auto& observer : observers_) { |
| observer.OnActiveUserSessionChanged( |
| user_sessions_[0]->user_info.account_id); |
| } |
| |
| UpdateLoginStatus(); |
| } |
| } |
| |
| void SessionControllerImpl::PrepareForLock(PrepareForLockCallback callback) { |
| FullscreenController::MaybeExitFullscreen(); |
| std::move(callback).Run(); |
| } |
| |
| void SessionControllerImpl::StartLock(StartLockCallback callback) { |
| DCHECK(start_lock_callback_.is_null()); |
| start_lock_callback_ = std::move(callback); |
| |
| LockStateController* const lock_state_controller = |
| Shell::Get()->lock_state_controller(); |
| |
| lock_state_controller->SetLockScreenDisplayedCallback( |
| base::BindOnce(&SessionControllerImpl::OnLockAnimationFinished, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void SessionControllerImpl::NotifyChromeLockAnimationsComplete() { |
| Shell::Get()->power_event_observer()->OnLockAnimationsComplete(); |
| } |
| |
| void SessionControllerImpl::RunUnlockAnimation( |
| RunUnlockAnimationCallback callback) { |
| is_unlocking_ = true; |
| |
| // Shell could have no instance in tests. |
| if (Shell::HasInstance()) |
| Shell::Get()->lock_state_controller()->OnLockScreenHide( |
| std::move(callback)); |
| } |
| |
| void SessionControllerImpl::NotifyChromeTerminating() { |
| for (auto& observer : observers_) |
| observer.OnChromeTerminating(); |
| } |
| |
| void SessionControllerImpl::SetSessionLengthLimit(base::TimeDelta length_limit, |
| base::Time start_time) { |
| session_length_limit_ = length_limit; |
| session_start_time_ = start_time; |
| for (auto& observer : observers_) |
| observer.OnSessionLengthLimitChanged(); |
| } |
| |
| void SessionControllerImpl::CanSwitchActiveUser( |
| CanSwitchActiveUserCallback callback) { |
| Shell::Get()->screen_switch_check_controller()->CanSwitchAwayFromActiveUser( |
| std::move(callback)); |
| } |
| |
| void SessionControllerImpl::ShowMultiprofilesIntroDialog( |
| ShowMultiprofilesIntroDialogCallback callback) { |
| MultiprofilesIntroDialog::Show(std::move(callback)); |
| } |
| |
| void SessionControllerImpl::ShowTeleportWarningDialog( |
| ShowTeleportWarningDialogCallback callback) { |
| TeleportWarningDialog::Show(std::move(callback)); |
| } |
| |
| void SessionControllerImpl::ShowMultiprofilesSessionAbortedDialog( |
| const std::string& user_email) { |
| SessionAbortedDialog::Show(user_email); |
| } |
| |
| void SessionControllerImpl::AddSessionActivationObserverForAccountId( |
| const AccountId& account_id, |
| SessionActivationObserver* observer) { |
| bool locked = state_ == SessionState::LOCKED; |
| observer->OnLockStateChanged(locked); |
| observer->OnSessionActivated(user_sessions_.size() && |
| user_sessions_[0]->user_info.account_id == |
| account_id); |
| session_activation_observer_holder_.AddForAccountId(account_id, observer); |
| } |
| |
| void SessionControllerImpl::RemoveSessionActivationObserverForAccountId( |
| const AccountId& account_id, |
| SessionActivationObserver* observer) { |
| session_activation_observer_holder_.RemoveForAccountId(account_id, observer); |
| } |
| |
| void SessionControllerImpl::ClearUserSessionsForTest() { |
| user_sessions_.clear(); |
| last_active_user_prefs_ = nullptr; |
| active_session_id_ = 0u; |
| primary_session_id_ = 0u; |
| } |
| |
| void SessionControllerImpl::SetIsDemoSession() { |
| if (is_demo_session_) |
| return; |
| |
| is_demo_session_ = true; |
| Shell::Get()->metrics()->StartDemoSessionMetricsRecording(); |
| // Notifications should be silenced during demo sessions. |
| message_center::MessageCenter::Get()->SetQuietMode(true); |
| } |
| |
| void SessionControllerImpl::SetSessionState(SessionState state) { |
| if (state_ == state) |
| return; |
| |
| const bool was_user_session_blocked = IsUserSessionBlocked(); |
| const bool was_locked = state_ == SessionState::LOCKED; |
| state_ = state; |
| for (auto& observer : observers_) |
| observer.OnSessionStateChanged(state_); |
| |
| UpdateLoginStatus(); |
| |
| const bool locked = state_ == SessionState::LOCKED; |
| if (was_locked != locked) { |
| if (!locked) |
| is_unlocking_ = false; |
| |
| for (auto& observer : observers_) |
| observer.OnLockStateChanged(locked); |
| |
| session_activation_observer_holder_.NotifyLockStateChanged(locked); |
| } |
| |
| EnsureSigninScreenPrefService(); |
| |
| if (was_user_session_blocked && !IsUserSessionBlocked()) |
| EnsureActiveWindowAfterUnblockingUserSession(); |
| } |
| |
| void SessionControllerImpl::AddUserSession(const UserSession& user_session) { |
| if (primary_session_id_ == 0u) |
| primary_session_id_ = user_session.session_id; |
| |
| user_sessions_.push_back(std::make_unique<UserSession>(user_session)); |
| |
| const AccountId account_id(user_session.user_info.account_id); |
| PrefService* user_prefs = GetUserPrefServiceForUser(account_id); |
| // |user_prefs| could be null in tests. |
| if (user_prefs) |
| OnProfilePrefServiceInitialized(account_id, user_prefs); |
| |
| UpdateLoginStatus(); |
| for (auto& observer : observers_) |
| observer.OnUserSessionAdded(account_id); |
| } |
| |
| LoginStatus SessionControllerImpl::CalculateLoginStatus() const { |
| // TODO(jamescook|xiyuan): There is not a 1:1 mapping of SessionState to |
| // LoginStatus. Fix the cases that don't match. http://crbug.com/701193 |
| switch (state_) { |
| case SessionState::UNKNOWN: |
| case SessionState::OOBE: |
| case SessionState::LOGIN_PRIMARY: |
| case SessionState::LOGGED_IN_NOT_ACTIVE: |
| return LoginStatus::NOT_LOGGED_IN; |
| |
| case SessionState::ACTIVE: |
| return CalculateLoginStatusForActiveSession(); |
| |
| case SessionState::LOCKED: |
| return LoginStatus::LOCKED; |
| |
| case SessionState::LOGIN_SECONDARY: |
| // TODO: There is no LoginStatus for this. |
| return LoginStatus::USER; |
| } |
| NOTREACHED(); |
| return LoginStatus::NOT_LOGGED_IN; |
| } |
| |
| LoginStatus SessionControllerImpl::CalculateLoginStatusForActiveSession() |
| const { |
| DCHECK(state_ == SessionState::ACTIVE); |
| |
| if (user_sessions_.empty()) // Can be empty in tests. |
| return LoginStatus::USER; |
| |
| switch (user_sessions_[0]->user_info.type) { |
| case user_manager::USER_TYPE_REGULAR: |
| return LoginStatus::USER; |
| case user_manager::USER_TYPE_GUEST: |
| return LoginStatus::GUEST; |
| case user_manager::USER_TYPE_PUBLIC_ACCOUNT: |
| return LoginStatus::PUBLIC; |
| case user_manager::USER_TYPE_SUPERVISED: |
| return LoginStatus::SUPERVISED; |
| case user_manager::USER_TYPE_KIOSK_APP: |
| return LoginStatus::KIOSK_APP; |
| case user_manager::USER_TYPE_CHILD: |
| return LoginStatus::SUPERVISED; |
| case user_manager::USER_TYPE_ARC_KIOSK_APP: |
| return LoginStatus::KIOSK_APP; |
| case user_manager::USER_TYPE_ACTIVE_DIRECTORY: |
| // TODO: There is no LoginStatus for this. |
| return LoginStatus::USER; |
| case user_manager::USER_TYPE_WEB_KIOSK_APP: |
| return LoginStatus::KIOSK_APP; |
| case user_manager::NUM_USER_TYPES: |
| // Avoid having a "default" case so the compiler catches new enum values. |
| NOTREACHED(); |
| return LoginStatus::USER; |
| } |
| NOTREACHED(); |
| return LoginStatus::USER; |
| } |
| |
| void SessionControllerImpl::UpdateLoginStatus() { |
| const LoginStatus new_login_status = CalculateLoginStatus(); |
| if (new_login_status == login_status_) |
| return; |
| |
| login_status_ = new_login_status; |
| for (auto& observer : observers_) |
| observer.OnLoginStatusChanged(login_status_); |
| } |
| |
| void SessionControllerImpl::OnLockAnimationFinished() { |
| if (!start_lock_callback_.is_null()) |
| std::move(start_lock_callback_).Run(true /* locked */); |
| } |
| |
| void SessionControllerImpl::EnsureSigninScreenPrefService() { |
| // Obtain and notify signin profile prefs only once. |
| if (signin_screen_prefs_obtained_) |
| return; |
| |
| PrefService* const signin_prefs = GetSigninScreenPrefService(); |
| if (!signin_prefs) |
| return; |
| |
| OnSigninScreenPrefServiceInitialized(signin_prefs); |
| } |
| |
| void SessionControllerImpl::OnSigninScreenPrefServiceInitialized( |
| PrefService* pref_service) { |
| DCHECK(pref_service); |
| DCHECK(!signin_screen_prefs_obtained_); |
| |
| signin_screen_prefs_obtained_ = true; |
| |
| for (auto& observer : observers_) |
| observer.OnSigninScreenPrefServiceInitialized(pref_service); |
| |
| if (on_active_user_prefs_changed_notify_deferred_) { |
| // Notify obsevers with the deferred OnActiveUserPrefServiceChanged(). Do |
| // this in a separate loop from the above since observers might depend on |
| // each other and we want to avoid having inconsistent states. |
| for (auto& observer : observers_) |
| observer.OnActiveUserPrefServiceChanged(last_active_user_prefs_); |
| on_active_user_prefs_changed_notify_deferred_ = false; |
| } |
| } |
| |
| void SessionControllerImpl::OnProfilePrefServiceInitialized( |
| const AccountId& account_id, |
| PrefService* pref_service) { |
| // |pref_service| can be null in tests. |
| if (!pref_service) |
| return; |
| |
| DCHECK(!user_sessions_.empty()); |
| if (account_id == user_sessions_[0]->user_info.account_id) { |
| last_active_user_prefs_ = pref_service; |
| |
| MaybeNotifyOnActiveUserPrefServiceChanged(); |
| } |
| } |
| |
| void SessionControllerImpl::MaybeNotifyOnActiveUserPrefServiceChanged() { |
| DCHECK(last_active_user_prefs_); |
| |
| if (!signin_screen_prefs_obtained_) { |
| // We must guarantee that OnSigninScreenPrefServiceInitialized() is called |
| // before OnActiveUserPrefServiceChanged(), so defer notifying the |
| // observers until the sign in prefs are received. |
| on_active_user_prefs_changed_notify_deferred_ = true; |
| return; |
| } |
| |
| for (auto& observer : observers_) |
| observer.OnActiveUserPrefServiceChanged(last_active_user_prefs_); |
| } |
| |
| void SessionControllerImpl::EnsureActiveWindowAfterUnblockingUserSession() { |
| // This happens only in tests (See SessionControllerImplTest). |
| if (!Shell::HasInstance()) |
| return; |
| |
| auto mru_list = |
| Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk); |
| if (!mru_list.empty()) |
| mru_list.front()->Focus(); |
| } |
| |
| } // namespace ash |