blob: 6db51b6f24993e16b8ccaff12e61ff69bbc5f279 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/session_manager/core/session_manager.h"
#include <algorithm>
#include "base/check.h"
#include "base/check_deref.h"
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/session_manager/core/session.h"
#include "components/session_manager/core/session_manager_delegate.h"
#include "components/session_manager/core/session_manager_observer.h"
#include "components/user_manager/user_manager.h"
namespace session_manager {
// static
SessionManager* SessionManager::instance = nullptr;
SessionManager::SessionManager(
std::unique_ptr<session_manager::SessionManagerDelegate> delegate)
: delegate_(std::move(delegate)) {
CHECK(delegate_);
DCHECK(!SessionManager::Get());
SessionManager::SetInstance(this);
}
SessionManager::~SessionManager() {
DCHECK_EQ(instance, this);
SessionManager::SetInstance(nullptr);
}
// static
SessionManager* SessionManager::Get() {
return SessionManager::instance;
}
void SessionManager::SetSessionState(SessionState state) {
if (session_state_ == state)
return;
VLOG(1) << "Changing session state to: " << static_cast<int>(state);
session_state_ = state;
for (auto& observer : observers_)
observer.OnSessionStateChanged();
}
void SessionManager::CreateSession(const AccountId& user_account_id,
const std::string& username_hash,
bool new_user,
bool has_active_session) {
// For secondary user log-in in common cases, we switch the active
// session after user Profile is created, so data needed for UI update,
// such as wallpaper, can be ready on activation.
// We do not switch if this is for recovering multi-sign-in user sessions
// because in the case we do not switch every user on starting, but
// only for the last active user after all sessions are created.
if (!has_active_session && !sessions_.empty()) {
pending_active_account_id_ = user_account_id;
}
CreateSessionInternal(user_account_id, username_hash, new_user,
/*browser_restart=*/false);
}
void SessionManager::CreateSessionForRestart(const AccountId& user_account_id,
const std::string& username_hash,
bool new_user) {
CreateSessionInternal(user_account_id, username_hash, new_user,
/*browser_restart=*/true);
}
void SessionManager::SwitchActiveSession(const AccountId& account_id) {
CHECK(user_manager_);
CHECK(HasSessionForAccountId(account_id));
user_manager_->SwitchActiveUser(account_id);
}
void SessionManager::RequestSignOut() {
delegate_->RequestSignOut();
}
void SessionManager::OnUserManagerCreated(
user_manager::UserManager* user_manager) {
user_manager_ = user_manager;
user_manager_observation_.Observe(user_manager_);
}
bool SessionManager::IsSessionStarted() const {
return session_started_;
}
bool SessionManager::IsUserSessionStartUpTaskCompleted() const {
return user_session_start_up_task_completed_;
}
void SessionManager::SessionStarted() {
TRACE_EVENT0("login", "SessionManager::SessionStarted");
session_started_ = true;
bool is_primary = sessions_.size() == 1;
for (auto& observer : observers_)
observer.OnUserSessionStarted(is_primary);
}
bool SessionManager::HasSessionForAccountId(const AccountId& account_id) const {
return FindSession(account_id) != nullptr;
}
const Session* SessionManager::FindSession(const AccountId& account_id) const {
auto it = std::ranges::find(sessions_, account_id, [](const auto& session) {
return session->account_id();
});
return it == sessions_.end() ? nullptr : it->get();
}
const Session* SessionManager::GetActiveSession() const {
CHECK(user_manager_);
const auto* active_user = user_manager_->GetActiveUser();
if (!active_user) {
return nullptr;
}
return FindSession(active_user->GetAccountId());
}
const Session* SessionManager::GetPrimarySession() const {
CHECK(user_manager_);
const auto* primary_user = user_manager_->GetPrimaryUser();
CHECK_EQ(!!primary_user, !sessions_.empty());
if (sessions_.empty()) {
return nullptr;
}
const Session* primary_session = sessions_[0].get();
CHECK_EQ(primary_user->GetAccountId(), primary_session->account_id());
return primary_session;
}
bool SessionManager::IsInSecondaryLoginScreen() const {
return session_state_ == SessionState::LOGIN_SECONDARY;
}
bool SessionManager::IsScreenLocked() const {
return session_state_ == SessionState::LOCKED;
}
bool SessionManager::IsUserSessionBlocked() const {
return session_state_ != SessionState::ACTIVE;
}
void SessionManager::AddObserver(SessionManagerObserver* observer) {
observers_.AddObserver(observer);
}
void SessionManager::RemoveObserver(SessionManagerObserver* observer) {
observers_.RemoveObserver(observer);
}
void SessionManager::NotifyUserProfileLoaded(const AccountId& account_id) {
for (auto& observer : observers_)
observer.OnUserProfileLoaded(account_id);
}
void SessionManager::NotifyLoginOrLockScreenVisible() {
login_or_lock_screen_shown_for_test_ = true;
for (auto& observer : observers_)
observer.OnLoginOrLockScreenVisible();
}
void SessionManager::NotifyUnlockAttempt(const bool success,
const UnlockType unlock_type) {
for (auto& observer : observers_)
observer.OnUnlockScreenAttempt(success, unlock_type);
}
void SessionManager::HandleUserSessionStartUpTaskCompleted() {
// This method must not be called twice.
CHECK(!user_session_start_up_task_completed_);
user_session_start_up_task_completed_ = true;
for (auto& observer : observers_) {
observer.OnUserSessionStartUpTaskCompleted();
}
}
// Note: there're *two* types of timing that are considered "profile created".
// 1) ProfileManager has responsibility to create a Profile
// then it invokes ProfileManagerObserver::OnProfileAdded().
// UserManager::OnUserProfileCreated() is called at the timing.
// 2) After finishing Profile creation, there are several more tasks to
// run for ash-chrome's Profile initialization. After all the tasks
// UserSessionManager (indirectly) calls NotifyUserProfileLoaded(). This
// practically happens after 1).
// The gap of the timing looks source of the confusion. We probably want to
// revisit here to unify the timing of "profile creation completion".
void SessionManager::OnUserProfileCreated(const user_manager::User& user) {
if (pending_active_account_id_.is_valid()) {
user_manager_->SwitchActiveUser(
std::exchange(pending_active_account_id_, EmptyAccountId()));
}
}
// static
void SessionManager::SetInstance(SessionManager* session_manager) {
SessionManager::instance = session_manager;
}
void SessionManager::CreateSessionInternal(const AccountId& user_account_id,
const std::string& username_hash,
bool new_user,
bool browser_restart) {
CHECK(user_manager_);
DCHECK(!HasSessionForAccountId(user_account_id));
const auto& user = CHECK_DEREF(user_manager_->FindUser(user_account_id));
// TODO(crbug.com/278643115): This attribute looks like the one for Session
// rather than UserManager. Move the field.
// Note: For KioskApp user, this may be updated later in UserSessionManager.
user_manager_->SetIsCurrentUserNew(
(new_user && user.HasGaiaAccount()) ||
user.GetType() == user_manager::UserType::kPublicAccount);
observers_.Notify(&SessionManagerObserver::OnSessionCreationStarted,
user_account_id);
sessions_.push_back(std::make_unique<Session>(next_id_++, user_account_id));
user_manager_->UserLoggedIn(user_account_id, username_hash);
// The created sessions and logged-in users in UserManager should be the
// same list.
const auto& logged_in_users = user_manager_->GetLoggedInUsers();
CHECK_EQ(sessions_.size(), logged_in_users.size());
for (size_t i = 0; i < sessions_.size(); ++i) {
CHECK_EQ(sessions_[i]->account_id(), logged_in_users[i]->GetAccountId());
}
OnSessionCreated(browser_restart);
observers_.Notify(&SessionManagerObserver::OnSessionCreated, user_account_id);
}
} // namespace session_manager