910e62b5创建于 1月15日历史提交
// Copyright 2017 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/session/test_session_controller_client.h"

#include <ranges>
#include <string>

#include "ash/login/login_screen_controller.h"
#include "ash/login_status.h"
#include "ash/session/session_controller_impl.h"
#include "ash/session/test_pref_service_provider.h"
#include "ash/shell.h"
#include "ash/wallpaper/wallpaper_controller_impl.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "components/account_id/account_id.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/testing_pref_service.h"
#include "components/session_manager/session_manager_types.h"
#include "components/user_manager/user_type.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "ui/views/widget/widget.h"

namespace ash {

TestSessionControllerClient::TestSessionControllerClient(
    SessionControllerImpl* controller,
    TestPrefServiceProvider* prefs_provider,
    bool create_signin_pref_service)
    : controller_(controller), prefs_provider_(prefs_provider) {
  CHECK(controller_);
  CHECK(prefs_provider_);
  Reset();

  if (create_signin_pref_service) {
    prefs_provider_->CreateSigninPrefsIfNeeded();
  }
}

TestSessionControllerClient::~TestSessionControllerClient() = default;

void TestSessionControllerClient::InitializeAndSetClient() {
  session_info_.can_lock_screen = controller_->CanLockScreen();
  session_info_.should_lock_screen_automatically =
      controller_->ShouldLockScreenAutomatically();
  session_info_.add_user_session_policy = controller_->GetAddUserPolicy();
  session_info_.state = controller_->GetSessionState();

  controller_->SetClient(this);
  controller_->EnsureSigninScreenPrefService();
}

void TestSessionControllerClient::Reset() {
  session_info_.can_lock_screen = true;
  session_info_.should_lock_screen_automatically = false;
  session_info_.add_user_session_policy = AddUserSessionPolicy::ALLOWED;
  session_info_.state = session_manager::SessionState::LOGIN_PRIMARY;
  first_session_ready_fired_ = false;
  // Emulate using the same pref service when re-login.
  reuse_pref_service_ = true;

  controller_->ClearUserSessionsForTest();
  controller_->SetSessionInfo(session_info_);
}

void TestSessionControllerClient::SetCanLockScreen(bool can_lock) {
  session_info_.can_lock_screen = can_lock;
  controller_->SetSessionInfo(session_info_);
}

void TestSessionControllerClient::SetShouldLockScreenAutomatically(
    bool should_lock) {
  session_info_.should_lock_screen_automatically = should_lock;
  controller_->SetSessionInfo(session_info_);
}

void TestSessionControllerClient::SetAddUserSessionPolicy(
    AddUserSessionPolicy policy) {
  session_info_.add_user_session_policy = policy;
  controller_->SetSessionInfo(session_info_);
}

void TestSessionControllerClient::SetSessionState(
    session_manager::SessionState state) {
  session_info_.state = state;
  controller_->SetSessionInfo(session_info_);

  MaybeNotifyFirstSessionReady();
}

void TestSessionControllerClient::SetIsRunningInAppMode(bool app_mode) {
  session_info_.is_running_in_app_mode = app_mode;
  controller_->SetSessionInfo(session_info_);
}

void TestSessionControllerClient::SetIsDemoSession() {
  session_info_.is_demo_session = true;
  controller_->SetSessionInfo(session_info_);
}

AccountId TestSessionControllerClient::AddUserSession(
    LoginInfo login_info,
    std::optional<AccountId> opt_account_id,
    std::unique_ptr<PrefService> pref_service) {
  CHECK(opt_account_id || login_info.display_email);

  std::string_view display_email = login_info.display_email.value_or(
      opt_account_id ? opt_account_id->GetUserEmail() : std::string_view());

  // value_or can't be used because user_email may be nullopt.
  AccountId account_id = opt_account_id.value_or(
      AccountId::FromUserEmail(gaia::CanonicalizeEmail(display_email)));

  // When both `account_id` and `display_email` is specified, the account_id's
  // user email must be same as cannonicalized display email.
  CHECK_EQ(account_id.GetUserEmail(), gaia::CanonicalizeEmail(display_email));

  // There must not exist the user session for the user passed here.
  CHECK(!controller_->GetUserSessionByAccountId(account_id)) << account_id;

  // Set is_ephemeral in user_info to true if the user type is guest or public
  // account.
  bool is_ephemeral =
      login_info.user_type == user_manager::UserType::kGuest ||
      login_info.user_type == user_manager::UserType::kPublicAccount ||
      login_info.is_ephemeral;
  if (pref_service_must_exist_) {
    CHECK(!pref_service);
    CHECK(GetUserPrefService(account_id));
  } else if (pref_service) {
    CHECK(!controller_->GetUserPrefServiceForUser(account_id));
    prefs_provider_->SetUserPrefs(account_id, std::move(pref_service));
  } else if (!GetUserPrefService(account_id)) {
    prefs_provider_->SetUserPrefs(
        account_id, TestPrefServiceProvider::CreateUserPrefServiceSimple());
  } else {
    CHECK(reuse_pref_service_);
    CHECK(GetUserPrefService(account_id));
  }

  UserSession session;
  session.session_id = ++fake_session_id_;
  session.user_info.type = login_info.user_type;
  session.user_info.account_id = account_id;
  session.user_info.display_name = "über tray über tray über tray über tray";
  session.user_info.display_email = display_email;
  session.user_info.is_ephemeral = is_ephemeral;
  session.user_info.is_new_profile = login_info.is_new_profile;
  session.user_info.given_name =
      login_info.given_name.value_or(std::string_view());
  session.user_info.has_gaia_account =
      account_id.GetAccountType() == AccountType::GOOGLE &&
      !account_id.GetGaiaId().empty();
  session.user_info.is_managed = login_info.is_account_managed;
  controller_->UpdateUserSession(std::move(session));

  MaybeNotifyFirstSessionReady();
  return account_id;
}

void TestSessionControllerClient::LockScreen() {
  RequestLockScreen();
  FlushForTest();
}

void TestSessionControllerClient::UnlockScreen() {
  RequestHideLockScreen();
}

void TestSessionControllerClient::FlushForTest() {
  base::RunLoop().RunUntilIdle();
}

void TestSessionControllerClient::SetSigninScreenPrefService(
    std::unique_ptr<PrefService> pref_service) {
  prefs_provider_->SetSigninPrefs(std::move(pref_service));
  controller_->OnSigninScreenPrefServiceInitialized(
      prefs_provider_->GetSigninPrefs());
}

void TestSessionControllerClient::SetUnownedUserPrefService(
    const AccountId& account_id,
    raw_ptr<PrefService> unowned_pref_service) {
  CHECK(!controller_->GetUserPrefServiceForUser(account_id));

  prefs_provider_->SetUnownedUserPrefs(account_id,
                                       std::move(unowned_pref_service));
}

void TestSessionControllerClient::RequestLockScreen() {
  if (should_show_lock_screen_) {
    // The lock screen can't be shown without a wallpaper.
    Shell::Get()->wallpaper_controller()->ShowDefaultWallpaperForTesting();
    Shell::Get()->login_screen_controller()->ShowLockScreen();
  }

  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, base::BindOnce(&TestSessionControllerClient::SetSessionState,
                                weak_ptr_factory_.GetWeakPtr(),
                                session_manager::SessionState::LOCKED));
}

void TestSessionControllerClient::RequestHideLockScreen() {
  ++request_hide_lock_screen_count_;
  SetSessionState(session_manager::SessionState::ACTIVE);
}

void TestSessionControllerClient::RequestSignOut() {
  Reset();
  ++request_sign_out_count_;
}

void TestSessionControllerClient::RequestRestartForUpdate() {
  ++request_restart_for_update_count_;
}

void TestSessionControllerClient::AttemptRestartChrome() {
  ++attempt_restart_chrome_count_;
}

void TestSessionControllerClient::SwitchActiveUser(
    const AccountId& account_id) {
  controller_->CanSwitchActiveUser(
      base::BindOnce(&TestSessionControllerClient::DoSwitchUser,
                     weak_ptr_factory_.GetWeakPtr(), account_id));
}

void TestSessionControllerClient::CycleActiveUser(
    CycleUserDirection direction) {
  DCHECK_GT(controller_->NumberOfLoggedInUsers(), 0);

  const SessionControllerImpl::UserSessions& sessions =
      controller_->GetUserSessions();
  const size_t session_count = sessions.size();

  // The following code depends on that |fake_session_id_| is used to generate
  // session ids.
  uint32_t session_id = sessions[0]->session_id;
  if (direction == CycleUserDirection::NEXT) {
    ++session_id;
  } else if (direction == CycleUserDirection::PREVIOUS) {
    DCHECK_GT(session_id, 0u);
    --session_id;
  } else {
    LOG(ERROR) << "Invalid cycle user direction="
               << static_cast<int>(direction);
    return;
  }

  // Valid range of an session id is [1, session_count].
  if (session_id < 1u)
    session_id = session_count;
  if (session_id > session_count)
    session_id = 1u;

  // Maps session id to AccountId and call SwitchActiveUser.
  auto it = std::ranges::find_if(
      sessions, [session_id](const std::unique_ptr<UserSession>& session) {
        return session && session->session_id == session_id;
      });
  if (it == sessions.end()) {
    NOTREACHED();
  }

  SwitchActiveUser((*it)->user_info.account_id);
}

void TestSessionControllerClient::ShowMultiProfileLogin() {
  SetSessionState(session_manager::SessionState::LOGIN_SECONDARY);

  views::Widget::InitParams params(
      views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
  params.bounds = gfx::Rect(0, 0, 400, 300);
  params.context = Shell::GetPrimaryRootWindow();

  multi_profile_login_widget_ = std::make_unique<views::Widget>();
  multi_profile_login_widget_->Init(std::move(params));
  multi_profile_login_widget_->Show();
}

void TestSessionControllerClient::EmitAshInitialized() {}

PrefService* TestSessionControllerClient::GetSigninScreenPrefService() {
  return prefs_provider_->GetSigninPrefs();
}

PrefService* TestSessionControllerClient::GetUserPrefService(
    const AccountId& account_id) {
  return prefs_provider_->GetUserPrefs(account_id);
}

base::FilePath TestSessionControllerClient::GetProfilePath(
    const AccountId& account_id) {
  return base::FilePath("/profile/path").Append(account_id.GetUserEmail());
}

std::tuple<bool, bool> TestSessionControllerClient::IsEligibleForSeaPen(
    const AccountId& account_id) {
  return is_eligible_for_background_replace_;
}

std::optional<int> TestSessionControllerClient::GetExistingUsersCount() const {
  return existing_users_count_;
}

int TestSessionControllerClient::NumberOfLoggedInUsers() const {
  // This should be migrated to GetExistingUserCount when
  // TestSessionControllerImpl is removed.
  return controller_->NumberOfLoggedInUsers();
}

void TestSessionControllerClient::DoSwitchUser(const AccountId& account_id,
                                               bool switch_user) {
  if (!switch_user)
    return;

  std::vector<uint32_t> session_order;
  session_order.reserve(controller_->GetUserSessions().size());

  for (const auto& user_session : controller_->GetUserSessions()) {
    if (user_session->user_info.account_id == account_id) {
      session_order.insert(session_order.begin(), user_session->session_id);
    } else {
      session_order.push_back(user_session->session_id);
    }
  }

  controller_->SetUserSessionOrder(session_order);
}

void TestSessionControllerClient::MaybeNotifyFirstSessionReady() {
  if (!first_session_ready_fired_ &&
      controller_->IsActiveUserSessionStarted() &&
      session_info_.state == session_manager::SessionState::ACTIVE) {
    first_session_ready_fired_ = true;
    controller_->NotifyFirstSessionReady();
  }
}

}  // namespace ash