[go: nahoru, domu]

blob: 2253e0aac27554e07906c349001689d56f58cdb1 [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 "chrome/browser/ash/login/session/user_session_manager.h"
#include <stddef.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "ash/components/arc/arc_prefs.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/metrics/login_unlock_throughput_recorder.h"
#include "ash/shell.h"
#include "base/base_paths.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/debug/alias.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/hash/sha1.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/path_service.h"
#include "base/scoped_observation.h"
#include "base/strings/string_number_conversions.h"
#include "base/syslog_logging.h"
#include "base/system/sys_info.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/version.h"
#include "chrome/browser/about_flags.h"
#include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/ash/account_manager/account_manager_util.h"
#include "chrome/browser/ash/app_list/app_list_client_impl.h"
#include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
#include "chrome/browser/ash/app_restore/full_restore_service.h"
#include "chrome/browser/ash/arc/arc_migration_guide_notification.h"
#include "chrome/browser/ash/arc/arc_util.h"
#include "chrome/browser/ash/base/locale_util.h"
#include "chrome/browser/ash/boot_times_recorder.h"
#include "chrome/browser/ash/child_accounts/child_policy_observer.h"
#include "chrome/browser/ash/crosapi/browser_data_back_migrator.h"
#include "chrome/browser/ash/crosapi/browser_data_migrator.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/eol_notification.h"
#include "chrome/browser/ash/first_run/first_run.h"
#include "chrome/browser/ash/floating_workspace/floating_workspace_service.h"
#include "chrome/browser/ash/floating_workspace/floating_workspace_util.h"
#include "chrome/browser/ash/hats/hats_config.h"
#include "chrome/browser/ash/logging.h"
#include "chrome/browser/ash/login/auth/chrome_safe_mode_delegate.h"
#include "chrome/browser/ash/login/chrome_restart_request.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
#include "chrome/browser/ash/login/existing_user_controller.h"
#include "chrome/browser/ash/login/helper.h"
#include "chrome/browser/ash/login/lock/screen_locker.h"
#include "chrome/browser/ash/login/login_pref_names.h"
#include "chrome/browser/ash/login/onboarding_user_activity_counter.h"
#include "chrome/browser/ash/login/profile_auth_data.h"
#include "chrome/browser/ash/login/quick_unlock/pin_backend.h"
#include "chrome/browser/ash/login/saml/password_sync_token_verifier.h"
#include "chrome/browser/ash/login/saml/password_sync_token_verifier_factory.h"
#include "chrome/browser/ash/login/screens/display_size_screen.h"
#include "chrome/browser/ash/login/screens/drive_pinning_screen.h"
#include "chrome/browser/ash/login/screens/sync_consent_screen.h"
#include "chrome/browser/ash/login/security_token_session_controller_factory.h"
#include "chrome/browser/ash/login/session/user_session_initializer.h"
#include "chrome/browser/ash/login/signin/oauth2_login_manager_factory.h"
#include "chrome/browser/ash/login/signin/offline_signin_limiter.h"
#include "chrome/browser/ash/login/signin/offline_signin_limiter_factory.h"
#include "chrome/browser/ash/login/signin/token_handle_fetcher.h"
#include "chrome/browser/ash/login/startup_utils.h"
#include "chrome/browser/ash/login/ui/input_events_blocker.h"
#include "chrome/browser/ash/login/ui/login_display_host.h"
#include "chrome/browser/ash/login/users/chrome_user_manager.h"
#include "chrome/browser/ash/login/wizard_controller.h"
#include "chrome/browser/ash/net/alwayson_vpn_pre_connect_url_allowlist_service.h"
#include "chrome/browser/ash/net/alwayson_vpn_pre_connect_url_allowlist_service_factory.h"
#include "chrome/browser/ash/notifications/update_notification.h"
#include "chrome/browser/ash/notifications/update_notification_showing_controller.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/handlers/adb_sideloading_allowance_mode_policy_handler.h"
#include "chrome/browser/ash/policy/handlers/minimum_version_policy_handler.h"
#include "chrome/browser/ash/policy/handlers/tpm_auto_update_mode_policy_handler.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/ash/profiles/signin_profile_handler.h"
#include "chrome/browser/ash/settings/about_flags.h"
#include "chrome/browser/ash/settings/cros_settings.h"
#include "chrome/browser/ash/settings/device_settings_service.h"
#include "chrome/browser/ash/system_web_apps/apps/help_app/help_app_notification_controller.h"
#include "chrome/browser/ash/tether/tether_service.h"
#include "chrome/browser/ash/tpm_firmware_update_notification.h"
#include "chrome/browser/ash/u2f_notification.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part_ash.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/lifetime/application_lifetime_chromeos.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/password_manager/profile_password_store_factory.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/chrome_device_id_helper.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_features.h"
#include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
#include "chrome/browser/sync/desk_sync_service_factory.h"
#include "chrome/browser/trusted_vault/trusted_vault_service_factory.h"
#include "chrome/browser/ui/startup/launch_mode_recorder.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/account_manager/account_manager_factory.h"
#include "chromeos/ash/components/assistant/buildflags.h"
#include "chromeos/ash/components/browser_context_helper/browser_context_flusher.h"
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
#include "chromeos/ash/components/login/auth/auth_session_authenticator.h"
#include "chromeos/ash/components/login/auth/authenticator_builder.h"
#include "chromeos/ash/components/login/auth/challenge_response/known_user_pref_utils.h"
#include "chromeos/ash/components/login/auth/public/user_context.h"
#include "chromeos/ash/components/login/auth/stub_authenticator_builder.h"
#include "chromeos/ash/components/login/hibernate/hibernate_manager.h"
#include "chromeos/ash/components/login/session/session_termination_manager.h"
#include "chromeos/ash/components/network/portal_detector/network_portal_detector.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/ash/components/tpm/prepare_tpm.h"
#include "chromeos/dbus/tpm_manager/tpm_manager.pb.h"
#include "chromeos/dbus/tpm_manager/tpm_manager_client.h"
#include "chromeos/ui/vector_icons/vector_icons.h"
#include "components/account_id/account_id.h"
#include "components/account_manager_core/account.h"
#include "components/account_manager_core/chromeos/account_manager.h"
#include "components/component_updater/component_updater_service.h"
#include "components/flags_ui/flags_ui_metrics.h"
#include "components/flags_ui/pref_service_flags_storage.h"
#include "components/language/core/browser/pref_names.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_store/password_store_interface.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/quirks/quirks_manager.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/session_manager_types.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h"
#include "components/signin/public/identity_manager/tribool.h"
#include "components/supervised_user/core/browser/child_account_service.h"
#include "components/trusted_vault/trusted_vault_client.h"
#include "components/trusted_vault/trusted_vault_service.h"
#include "components/user_manager/common_types.h"
#include "components/user_manager/known_user.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_names.h"
#include "components/user_manager/user_type.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_switches.h"
#include "extensions/common/features/feature_session_type.h"
#include "extensions/common/mojom/feature_session_type.mojom.h"
#include "rlz/buildflags/buildflags.h"
#include "third_party/cros_system_api/switches/chrome_switches.h"
#include "ui/base/ime/ash/input_method_descriptor.h"
#include "ui/base/ime/ash/input_method_manager.h"
#include "ui/base/ime/ash/input_method_util.h"
#include "url/gurl.h"
#undef ENABLED_VLOG_LEVEL
#define ENABLED_VLOG_LEVEL 1
namespace ash {
namespace {
using ::signin::ConsentLevel;
// Time to wait for child policy refresh. If that time is exceeded session
// should start with cached policy.
constexpr base::TimeDelta kWaitForChildPolicyTimeout = base::Seconds(10);
// Timeout to fetch flags from the child account service.
constexpr base::TimeDelta kFlagsFetchingLoginTimeout = base::Milliseconds(1000);
// Trace event category of the trace events.
constexpr char kEventCategoryChromeOS[] = "chromeos";
// Trace event that covers the time from UserSessionManager::StartSession is
// called until UserSessionManager notifies SessionManager::SessionStarted.
// Basically, the time after cryptohome mount until user desktop is about to be
// shown after animations triggered by session state changing to ACTIVE.
constexpr char kEventStartSession[] = "StartUserSession";
// Trace event that covers the time spend to notify session manager daemon
// about a new user session is starting.
constexpr char kEventStartCrosSession[] = "StartCrosSession";
// Trace event that covers the time prior user profile loading.
constexpr char kEventPrePrepareProfile[] = "PrePrepareProfile";
// Trace event that covers the time between start user profile loading and
// when user profile loading is finalized.
constexpr char kEventPrepareProfile[] = "PrepareProfile";
// Trace event that covers the time spent after user profile load but before
// start to prepare user desktop, e.g. notify user profile observers, start
// services that needs user profile.
constexpr char kEventHandleProfileLoad[] = "HandleProfileLoad";
// Trace event that covers the time to prepare user desktop, e.g. launching
// browser and dismiss the login screen. Note full restore is asynchronous and
// is not included.
constexpr char kEventInitUserDesktop[] = "InitUserDesktop";
constexpr base::TimeDelta kActivityTimeBeforeOnboardingSurvey = base::Hours(1);
// A special version used to backfill the OnboardingCompletedVersion for
// existing users to indicate that they are already completed the onboarding
// flow in unknown past version.
constexpr char kOnboardingBackfillVersion[] = "0.0.0.0";
base::TimeDelta GetActivityTimeBeforeOnboardingSurvey() {
auto* command_line = base::CommandLine::ForCurrentProcess();
const auto& time_switch =
switches::kTimeBeforeOnboardingSurveyInSecondsForTesting;
if (!command_line->HasSwitch(time_switch)) {
return kActivityTimeBeforeOnboardingSurvey;
}
int seconds;
if (!base::StringToInt(command_line->GetSwitchValueASCII(time_switch),
&seconds)) {
return kActivityTimeBeforeOnboardingSurvey;
}
if (seconds <= 0)
return kActivityTimeBeforeOnboardingSurvey;
return base::Seconds(seconds);
}
void InitLocaleAndInputMethodsForNewUser(
UserSessionManager* session_manager,
Profile* profile,
const std::string& public_session_locale,
const std::string& public_session_input_method) {
PrefService* prefs = profile->GetPrefs();
std::string locale;
if (!public_session_locale.empty()) {
// If this is a public session and the user chose a `public_session_locale`,
// write it to `prefs` so that the UI switches to it.
locale = public_session_locale;
prefs->SetString(language::prefs::kApplicationLocale, locale);
// Suppress the locale change dialog.
prefs->SetString(::prefs::kApplicationLocaleAccepted, locale);
} else {
// Otherwise, assume that the session will use the current UI locale.
locale = g_browser_process->GetApplicationLocale();
}
// First, we'll set kLanguagePreloadEngines.
auto* manager = input_method::InputMethodManager::Get();
input_method::InputMethodDescriptor preferred_input_method;
if (!public_session_input_method.empty()) {
// If this is a public session and the user chose a valid
// `public_session_input_method`, use it as the `preferred_input_method`.
const input_method::InputMethodDescriptor* const descriptor =
manager->GetInputMethodUtil()->GetInputMethodDescriptorFromId(
public_session_input_method);
if (descriptor) {
preferred_input_method = *descriptor;
} else {
LOG(WARNING) << "Public session is initialized with an invalid IME"
<< ", id=" << public_session_input_method;
}
}
// If `preferred_input_method` is not set, use the currently active input
// method.
if (preferred_input_method.id().empty()) {
preferred_input_method =
session_manager->GetDefaultIMEState(profile)->GetCurrentInputMethod();
const input_method::InputMethodDescriptor* descriptor =
manager->GetInputMethodUtil()->GetInputMethodDescriptorFromId(
manager->GetInputMethodUtil()->GetHardwareInputMethodIds()[0]);
// If the hardware input method's keyboard layout is the same as the
// default input method (e.g. from GaiaScreen), use the hardware input
// method. Note that the hardware input method can be non-login-able.
// Refer to the issue chrome-os-partner:48623.
if (descriptor && descriptor->keyboard_layout() ==
preferred_input_method.keyboard_layout()) {
preferred_input_method = *descriptor;
}
}
// Derive kLanguagePreloadEngines from `locale` and `preferred_input_method`.
std::vector<std::string> input_method_ids;
manager->GetInputMethodUtil()->GetFirstLoginInputMethodIds(
locale, preferred_input_method, &input_method_ids);
// Save the input methods in the user's preferences.
StringPrefMember language_preload_engines;
language_preload_engines.Init(::prefs::kLanguagePreloadEngines, prefs);
language_preload_engines.SetValue(base::JoinString(input_method_ids, ","));
BootTimesRecorder::Get()->AddLoginTimeMarker("IMEStarted", false);
// Second, we'll set kPreferredLanguages.
std::vector<std::string> language_codes;
// The current locale should be on the top.
language_codes.push_back(locale);
// Add input method IDs based on the input methods, as there may be
// input methods that are unrelated to the current locale. Example: the
// hardware keyboard layout xkb:us::eng is used for logging in, but the
// UI language is set to French. In this case, we should set "fr,en"
// to the preferred languages preference.
std::vector<std::string> candidates;
manager->GetInputMethodUtil()->GetLanguageCodesFromInputMethodIds(
input_method_ids, &candidates);
for (size_t i = 0; i < candidates.size(); ++i) {
const std::string& candidate = candidates[i];
// Add a candidate if it's not yet in language_codes and is allowed.
if (!base::Contains(language_codes, candidate) &&
locale_util::IsAllowedLanguage(candidate, prefs)) {
language_codes.push_back(candidate);
}
}
// Save the preferred languages in the user's preferences.
prefs->SetString(language::prefs::kPreferredLanguages,
base::JoinString(language_codes, ","));
// Indicate that we need to merge the syncable input methods when we sync,
// since we have not applied the synced prefs before.
prefs->SetBoolean(::prefs::kLanguageShouldMergeInputMethods, true);
}
bool CanPerformEarlyRestart() {
const ExistingUserController* controller =
ExistingUserController::current_controller();
if (!controller)
return true;
// Early restart is possible only if OAuth token is up to date.
if (controller->password_changed())
return false;
if (controller->auth_mode() != LoginPerformer::AuthorizationMode::kInternal)
return false;
return true;
}
void LogCustomFeatureFlags(const std::set<std::string>& feature_flags) {
if (VLOG_IS_ON(1)) {
for (const auto& feature_flag : feature_flags) {
VLOG(1) << "Feature flag leading to restart: '" << feature_flag << "'";
}
}
about_flags::ReadOnlyFlagsStorage flags_storage(feature_flags, {});
::about_flags::RecordUMAStatistics(&flags_storage, "Login.CustomFlags");
}
// Calls the real AttemptRestart method. This is used to avoid taking a function
// pointer to chrome::AttemptRestart directly.
void CallChromeAttemptRestart() {
chrome::AttemptRestart();
}
bool IsRunningTest() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kTestName) ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
::switches::kTestType);
}
bool IsOnlineSignin(const UserContext& user_context) {
return user_context.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITH_SAML ||
user_context.GetAuthFlow() == UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML;
}
// Stores the information about the challenge-response keys, that were used for
// authentication, persistently in the known_user database for future
// authentication attempts.
void PersistChallengeResponseKeys(const UserContext& user_context) {
user_manager::KnownUser(g_browser_process->local_state())
.SetChallengeResponseKeys(user_context.GetAccountId(),
SerializeChallengeResponseKeysForKnownUser(
user_context.GetChallengeResponseKeys()));
}
// Returns true if the user is new, or if the user was already present on the
// device and the profile was re-created. This can happen e.g. in ext4 migration
// in wipe mode.
bool IsNewProfile(Profile* profile) {
return user_manager::UserManager::Get()->IsCurrentUserNew() ||
profile->IsNewProfile();
}
policy::MinimumVersionPolicyHandler* GetMinimumVersionPolicyHandler() {
return g_browser_process->platform_part()
->browser_policy_connector_ash()
->GetMinimumVersionPolicyHandler();
}
void OnPrepareTpmDeviceFinished() {
BootTimesRecorder::Get()->AddLoginTimeMarker("TPMOwn-End", false);
}
void SaveSyncTrustedVaultKeysToProfile(
const std::string& gaia_id,
const SyncTrustedVaultKeys& trusted_vault_keys,
Profile* profile) {
trusted_vault::TrustedVaultService* trusted_vault_service =
TrustedVaultServiceFactory::GetForProfile(profile);
if (!trusted_vault_service) {
return;
}
if (!trusted_vault_keys.encryption_keys().empty()) {
trusted_vault_service
->GetTrustedVaultClient(trusted_vault::SecurityDomainId::kChromeSync)
->StoreKeys(gaia_id, trusted_vault_keys.encryption_keys(),
trusted_vault_keys.last_encryption_key_version());
}
for (const SyncTrustedVaultKeys::TrustedRecoveryMethod& method :
trusted_vault_keys.trusted_recovery_methods()) {
trusted_vault_service
->GetTrustedVaultClient(trusted_vault::SecurityDomainId::kChromeSync)
->AddTrustedRecoveryMethod(gaia_id, method.public_key, method.type_hint,
base::DoNothing());
}
}
bool IsHwDataUsageDeviceSettingSet() {
return DeviceSettingsService::Get() &&
DeviceSettingsService::Get()->device_settings() &&
DeviceSettingsService::Get()
->device_settings()
->has_hardware_data_usage_enabled();
}
// Updates local_state kOobeRevenUpdatedToFlex pref to true if OS was updated.
// Returns value of the kOobeRevenUpdatedToFlex pref.
bool IsRevenUpdatedToFlex() {
CHECK(switches::IsRevenBranding());
PrefService* local_state = g_browser_process->local_state();
if (local_state->GetBoolean(prefs::kOobeRevenUpdatedToFlex))
return true;
// If it is a first login after update from CloudReady this field in the
// device settings service won't be set.
bool is_hw_data_usage_enabled_already_set = IsHwDataUsageDeviceSettingSet();
// If this field isn't set it means that the device was updated to Flex
// and owner hasn't logged in yet. Set a boolean flag to control if the
// new terms should be shown for existing users on the device.
if (!is_hw_data_usage_enabled_already_set) {
local_state->SetBoolean(prefs::kOobeRevenUpdatedToFlex, true);
}
return local_state->GetBoolean(prefs::kOobeRevenUpdatedToFlex);
}
bool MaybeShowNewTermsAfterUpdateToFlex(Profile* profile) {
// Check if the device has been recently updated from CloudReady to show new
// license agreement and data collection consent. This applies only for
// existing users of not managed reven boards.
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (!switches::IsRevenBranding() || user_manager->IsCurrentUserNew()) {
return false;
}
// Reven devices can be updated from non-branded versions to Flex. Make sure
// that the EULA is marked as accepted when reven device is managed. For
// managed devices all the terms are accepted by the admin so we can simply
// mark it here.
policy::BrowserPolicyConnectorAsh* connector =
g_browser_process->platform_part()->browser_policy_connector_ash();
bool is_device_managed = connector->IsDeviceEnterpriseManaged();
if (is_device_managed) {
StartupUtils::MarkEulaAccepted();
network_portal_detector::GetInstance()->Enable();
return false;
}
if (!IsRevenUpdatedToFlex()) {
return false;
}
const bool should_show_new_terms =
(user_manager->IsCurrentUserOwner() &&
!IsHwDataUsageDeviceSettingSet()) ||
!profile->GetPrefs()->GetBoolean(
prefs::kRevenOobeConsolidatedConsentAccepted);
if (!should_show_new_terms) {
return false;
}
if (LoginDisplayHost::default_host() &&
LoginDisplayHost::default_host()->GetSigninUI()) {
LoginDisplayHost::default_host()->GetSigninUI()->ShowNewTermsForFlexUsers();
} else {
LOG(WARNING) << "Can't show additional terms of services for flex users as "
"LoginDisplayHost has been already destroyed!";
}
return true;
}
void RecordKnownUser(const AccountId& account_id) {
user_manager::KnownUser known_user(g_browser_process->local_state());
known_user.SaveKnownUser(account_id);
}
// Returns true if current browser instance was restarted in-session.
// I.e. restart after crash, restart to apply flags, etc.
bool IsAfterInSessionRestart() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
ash::switches::kLoginUser);
}
void MaybeSaveSessionStartedTimeBeforeRestart(Profile* profile) {
// Ignore if the browser restarted after crash.
if (IsAfterInSessionRestart()) {
return;
}
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (!user_manager) {
return;
}
PrefService* prefs = profile->GetPrefs();
prefs->ClearPref(ash::prefs::kAshLoginSessionStartedTime);
prefs->ClearPref(ash::prefs::kAshLoginSessionStartedIsFirstSession);
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(profile);
if (user_manager->GetPrimaryUser() != user) {
return;
}
// Record session started time before trying to restart.
prefs->SetTime(ash::prefs::kAshLoginSessionStartedTime, base::Time::Now());
if (user_manager->IsCurrentUserNew()) {
prefs->SetBoolean(ash::prefs::kAshLoginSessionStartedIsFirstSession, true);
}
}
// Returns a Base16 encoded SHA1 digest of `data`.
std::string Sha1Digest(const std::string& data) {
return base::HexEncode(base::SHA1HashSpan(base::as_byte_span(data)));
}
} // namespace
UserSessionManagerDelegate::~UserSessionManagerDelegate() {}
void UserSessionStateObserver::PendingUserSessionsRestoreFinished() {}
UserSessionStateObserver::~UserSessionStateObserver() {}
// static
UserSessionManager* UserSessionManager::GetInstance() {
return base::Singleton<UserSessionManager, base::DefaultSingletonTraits<
UserSessionManager>>::get();
}
// static
void UserSessionManager::OverrideHomedir() {
// Override user homedir, check for ProfileManager being initialized as
// it may not exist in unit tests.
if (g_browser_process->profile_manager()) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->GetLoggedInUsers().size() == 1) {
base::FilePath homedir = ProfileHelper::GetProfilePathByUserIdHash(
user_manager->GetPrimaryUser()->username_hash());
// This path has been either created by cryptohome (on real Chrome OS
// device) or by ProfileManager (on chromeos=1 desktop builds).
base::PathService::OverrideAndCreateIfNeeded(base::DIR_HOME, homedir,
true /* path is absolute */,
false /* don't create */);
}
}
}
// static
void UserSessionManager::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(::prefs::kRLZBrand, std::string());
registry->RegisterBooleanPref(::prefs::kRLZDisabled, false);
}
UserSessionManager::UserSessionManager()
: delegate_(nullptr),
network_connection_tracker_(nullptr),
authenticator_(nullptr),
has_auth_cookies_(false),
user_sessions_restored_(false),
user_sessions_restore_in_progress_(false),
should_obtain_handles_(true),
should_launch_browser_(true),
waiting_for_child_account_status_(false),
attempt_restart_closure_(base::BindRepeating(&CallChromeAttemptRestart)) {
user_manager::UserManager::Get()->AddSessionStateObserver(this);
user_manager::UserManager::Get()->AddObserver(this);
content::GetNetworkConnectionTrackerFromUIThread(
base::BindOnce(&UserSessionManager::SetNetworkConnectionTracker,
GetUserSessionManagerAsWeakPtr()));
}
UserSessionManager::~UserSessionManager() {
// UserManager is destroyed before singletons, so we need to check if it
// still exists.
// TODO(nkostylev): fix order of destruction of UserManager
// / UserSessionManager objects.
if (user_manager::UserManager::IsInitialized()) {
user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
user_manager::UserManager::Get()->RemoveObserver(this);
}
}
// Observes the Device Account's LST and informs UserSessionManager about it.
// Used by UserSessionManager to keep the user's token handle up to date.
class UserSessionManager::DeviceAccountGaiaTokenObserver
: public account_manager::AccountManager::Observer {
public:
DeviceAccountGaiaTokenObserver(
account_manager::AccountManager* account_manager,
const AccountId& account_id,
base::RepeatingCallback<void(const AccountId& account_id)> callback)
: account_id_(account_id), callback_(callback) {
account_manager_observation_.Observe(account_manager);
}
DeviceAccountGaiaTokenObserver(const DeviceAccountGaiaTokenObserver&) =
delete;
DeviceAccountGaiaTokenObserver& operator=(
const DeviceAccountGaiaTokenObserver&) = delete;
~DeviceAccountGaiaTokenObserver() override = default;
// account_manager::AccountManager::Observer overrides:
void OnTokenUpserted(const account_manager::Account& account) override {
if (account.key.account_type() != account_manager::AccountType::kGaia)
return;
if (account.key.id() != account_id_.GetGaiaId())
return;
callback_.Run(account_id_);
}
void OnAccountRemoved(const account_manager::Account& account) override {
// Device Account cannot be removed within session. We could have received
// this notification for a secondary account however, so consider this as a
// no-op.
}
private:
// The account being tracked by `this` instance.
const AccountId account_id_;
// `callback_` is called when `account_id`'s LST changes.
base::RepeatingCallback<void(const AccountId& account_id)> callback_;
base::ScopedObservation<account_manager::AccountManager,
account_manager::AccountManager::Observer>
account_manager_observation_{this};
};
void UserSessionManager::SetNetworkConnectionTracker(
network::NetworkConnectionTracker* network_connection_tracker) {
DCHECK(network_connection_tracker);
network_connection_tracker_ = network_connection_tracker;
network_connection_tracker_->AddLeakyNetworkConnectionObserver(this);
}
void UserSessionManager::SetShouldObtainHandleInTests(
bool should_obtain_handles) {
should_obtain_handles_ = should_obtain_handles;
if (!should_obtain_handles_) {
token_handle_fetcher_.reset();
}
}
void UserSessionManager::SetAttemptRestartClosureInTests(
const base::RepeatingClosure& attempt_restart_closure) {
attempt_restart_closure_ = attempt_restart_closure;
}
void UserSessionManager::CompleteGuestSessionLogin(const GURL& start_url) {
VLOG(1) << "Completing guest session login";
// For guest session we ask session_manager to restart Chrome with --bwsi
// flag. We keep only some of the arguments of this process.
const base::CommandLine& browser_command_line =
*base::CommandLine::ForCurrentProcess();
base::CommandLine command_line(browser_command_line.GetProgram());
GetOffTheRecordCommandLine(start_url, browser_command_line, &command_line);
// Trigger loading the shill profile before restarting.
// For regular user sessions, MGS or kiosk sessions, this is done by
// VoteForSavingLoginPassword.
LoadShillProfile(user_manager::GuestAccountId());
// This makes sure that Chrome restarts with no per-session flags. The guest
// profile will always have empty set of per-session flags. If this is not
// done and device owner has some per-session flags, when Chrome is relaunched
// the guest profile session flags will not match the current command line and
// another restart will be attempted in order to reset the user flags for the
// guest user.
SessionManagerClient::Get()->SetFeatureFlagsForUser(
cryptohome::CreateAccountIdentifierFromAccountId(
user_manager::GuestAccountId()),
/*feature_flags=*/{}, /*origin_list_flags=*/{});
RestartChrome(command_line, RestartChromeReason::kGuest);
}
scoped_refptr<Authenticator> UserSessionManager::CreateAuthenticator(
AuthStatusConsumer* consumer) {
// Screen locker needs new Authenticator instance each time.
if (ScreenLocker::default_screen_locker()) {
if (authenticator_.get())
authenticator_->SetConsumer(nullptr);
authenticator_.reset();
}
if (authenticator_.get() == nullptr) {
if (injected_authenticator_builder_) {
authenticator_ = injected_authenticator_builder_->Create(consumer);
} else {
authenticator_ = new AuthSessionAuthenticator(
consumer, std::make_unique<ChromeSafeModeDelegate>(),
base::BindRepeating(&RecordKnownUser),
g_browser_process->local_state());
}
} else {
// TODO(nkostylev): Fix this hack by improving Authenticator dependencies.
authenticator_->SetConsumer(consumer);
}
for (auto& observer : authenticator_observer_list_) {
observer.OnAuthAttemptStarted();
}
return authenticator_;
}
void UserSessionManager::StartSession(
const UserContext& user_context,
StartSessionType start_session_type,
bool has_auth_cookies,
bool has_active_session,
base::WeakPtr<UserSessionManagerDelegate> delegate) {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kEventCategoryChromeOS, kEventStartSession,
TRACE_ID_LOCAL(this));
delegate_ = std::move(delegate);
start_session_type_ = start_session_type;
VLOG(1) << "Starting user session.";
PreStartSession(start_session_type);
CreateUserSession(user_context, has_auth_cookies);
if (!has_active_session)
StartCrosSession();
user_manager::KnownUser known_user(g_browser_process->local_state());
// Note: Using `user_context_` here instead of `user_context`.
// `CreateUserSession()` call above copies the value of `user_context`
// (immutable) into `user_context_` (mutable).
InitializeDeviceId(user_manager::UserManager::Get()->IsEphemeralAccountId(
user_context_.GetAccountId()),
user_context_, known_user);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
kEventCategoryChromeOS, kEventPrePrepareProfile, TRACE_ID_LOCAL(this));
InitDemoSessionIfNeeded(base::BindOnce(
&UserSessionManager::UpdateArcFileSystemCompatibilityAndPrepareProfile,
GetUserSessionManagerAsWeakPtr()));
}
void UserSessionManager::PerformPostUserLoggedInActions() {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->GetLoggedInUsers().size() == 1) {
InitNonKioskExtensionFeaturesSessionType(user_manager->GetPrimaryUser());
}
}
void UserSessionManager::RestoreAuthenticationSession(Profile* user_profile) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
// We need to restore session only for logged in GAIA (regular) users.
// Note: stub user is a special case that is used for tests, running
// linux_chromeos build on dev workstations w/o user_id parameters.
// Stub user is considered to be a regular GAIA user but it has special
// user_id (kStubUser) and certain services like restoring OAuth session are
// explicitly disabled for it.
if (!user_manager->IsUserLoggedIn() ||
!user_manager->IsLoggedInAsUserWithGaiaAccount() ||
user_manager->IsLoggedInAsStub()) {
return;
}
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(user_profile);
auto* identity_manager = IdentityManagerFactory::GetForProfile(user_profile);
const bool account_id_valid =
identity_manager &&
!identity_manager->GetPrimaryAccountId(ConsentLevel::kSignin).empty();
if (!account_id_valid)
LOG(ERROR) << "No account is associated with sign-in manager on restore.";
DCHECK(user);
if (network_connection_tracker_ &&
!network_connection_tracker_->IsOffline()) {
pending_signin_restore_sessions_.erase(user->GetAccountId());
RestoreAuthSessionImpl(user_profile, false /* has_auth_cookies */);
} else {
// Even if we're online we should wait till initial
// OnConnectionTypeChanged() call. Otherwise starting fetchers too early may
// end up canceling all request when initial network connection type is
// processed. See http://crbug.com/121643.
pending_signin_restore_sessions_.insert(user->GetAccountId());
}
}
void UserSessionManager::RestoreActiveSessions() {
user_sessions_restore_in_progress_ = true;
SessionManagerClient::Get()->RetrieveActiveSessions(
base::BindOnce(&UserSessionManager::OnRestoreActiveSessions,
GetUserSessionManagerAsWeakPtr()));
}
bool UserSessionManager::UserSessionsRestored() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return user_sessions_restored_;
}
bool UserSessionManager::UserSessionsRestoreInProgress() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return user_sessions_restore_in_progress_;
}
void UserSessionManager::InitNonKioskExtensionFeaturesSessionType(
const user_manager::User* user) {
// Kiosk session should be set as part of kiosk user session initialization
// in normal circumstances (to be able to properly determine whether kiosk
// was auto-launched); in case of user session restore, feature session
// type has be set before kiosk app controller takes over, as at that point
// kiosk app profile would already be initialized - feature session type
// should be set before that.
if (user->IsKioskType()) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kLoginUser)) {
// For kiosk session crash recovery, feature session type has be set
// before kiosk app controller takes over, as at that point iosk app
// profile would already be initialized - feature session type
// should be set before that.
bool auto_launched = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAppAutoLaunched);
extensions::SetCurrentFeatureSessionType(
auto_launched
? extensions::mojom::FeatureSessionType::kAutolaunchedKiosk
: extensions::mojom::FeatureSessionType::kKiosk);
}
return;
}
extensions::SetCurrentFeatureSessionType(
user->HasGaiaAccount() ? extensions::mojom::FeatureSessionType::kRegular
: extensions::mojom::FeatureSessionType::kUnknown);
}
void UserSessionManager::SetFirstLoginPrefs(
Profile* profile,
const std::string& public_session_locale,
const std::string& public_session_input_method) {
VLOG(1) << "Setting first login prefs";
InitLocaleAndInputMethodsForNewUser(this, profile, public_session_locale,
public_session_input_method);
// Turn on the feature of the low battery sound for all users on the device
// when a new user login.
if (!g_browser_process->local_state()->IsManagedPreference(
prefs::kLowBatterySoundEnabled)) {
g_browser_process->local_state()->SetBoolean(prefs::kLowBatterySoundEnabled,
true);
}
}
void UserSessionManager::DoBrowserLaunch(Profile* profile) {
auto* session_manager = session_manager::SessionManager::Get();
const auto current_session_state = session_manager->session_state();
// LOGGED_IN_NOT_ACTIVE should only be set from OOBE, LOGIN_PRIMARY, or
// LOGIN_SECONDARY.
if (current_session_state == session_manager::SessionState::OOBE ||
current_session_state == session_manager::SessionState::LOGIN_PRIMARY ||
current_session_state == session_manager::SessionState::LOGIN_SECONDARY) {
session_manager->SetSessionState(
session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
}
ui_shown_time_ = base::Time::Now();
DoBrowserLaunchInternal(profile, /*locale_pref_checked=*/false);
}
bool UserSessionManager::RespectLocalePreference(
Profile* profile,
const user_manager::User* user,
locale_util::SwitchLanguageCallback callback) const {
// TODO(alemate): http://crbug.com/288941 : Respect preferred language list in
// the Google user profile.
if (g_browser_process == nullptr)
return false;
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (!user || (user_manager->IsUserLoggedIn() &&
user != user_manager->GetPrimaryUser())) {
return false;
}
// In case of multi-profiles session we don't apply profile locale
// because it is unsafe.
if (user_manager->GetLoggedInUsers().size() != 1)
return false;
PrefService* prefs = profile->GetPrefs();
if (prefs == nullptr)
return false;
std::string pref_locale;
const std::string pref_app_locale =
prefs->GetString(language::prefs::kApplicationLocale);
const std::string pref_bkup_locale =
prefs->GetString(::prefs::kApplicationLocaleBackup);
pref_locale = pref_app_locale;
// In Demo Mode, each sessions uses a new empty User Profile, so we need to
// rely on the local state set in the browser process.
if (DemoSession::IsDeviceInDemoMode() && pref_app_locale.empty()) {
const std::string local_state_locale =
g_browser_process->local_state()->GetString(
language::prefs::kApplicationLocale);
pref_locale = local_state_locale;
}
if (pref_locale.empty())
pref_locale = pref_bkup_locale;
const std::string* account_locale = nullptr;
if (pref_locale.empty() && user->has_gaia_account() &&
prefs->GetList(::prefs::kAllowedLanguages).empty()) {
if (user->GetAccountLocale() == nullptr)
return false; // wait until Account profile is loaded.
account_locale = user->GetAccountLocale();
pref_locale = *account_locale;
}
const std::string global_app_locale =
g_browser_process->GetApplicationLocale();
if (pref_locale.empty())
pref_locale = global_app_locale;
DCHECK(!pref_locale.empty());
VLOG(1) << "RespectLocalePreference: "
<< "app_locale='" << pref_app_locale << "', "
<< "bkup_locale='" << pref_bkup_locale << "', "
<< (account_locale != nullptr
? (std::string("account_locale='") + (*account_locale) +
"'. ")
: (std::string("account_locale - unused. ")))
<< " Selected '" << pref_locale << "'";
Profile::AppLocaleChangedVia app_locale_changed_via =
user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT
? Profile::APP_LOCALE_CHANGED_VIA_PUBLIC_SESSION_LOGIN
: Profile::APP_LOCALE_CHANGED_VIA_LOGIN;
// check if pref_locale is allowed by policy (AllowedLanguages)
if (!locale_util::IsAllowedUILanguage(pref_locale, prefs)) {
pref_locale = locale_util::GetAllowedFallbackUILanguage(prefs);
app_locale_changed_via = Profile::APP_LOCALE_CHANGED_VIA_POLICY;
}
profile->ChangeAppLocale(pref_locale, app_locale_changed_via);
// Here we don't enable keyboard layouts for normal users. Input methods
// are set up when the user first logs in. Then the user may customize the
// input methods. Hence changing input methods here, just because the user's
// UI language is different from the login screen UI language, is not
// desirable. Note that input method preferences are synced, so users can use
// their farovite input methods as soon as the preferences are synced.
//
// For Guest mode, user locale preferences will never get initialized.
// So input methods should be enabled somewhere.
const bool enable_layouts =
user_manager::UserManager::Get()->IsLoggedInAsGuest();
locale_util::SwitchLanguage(pref_locale, enable_layouts,
false /* login_layouts_only */,
std::move(callback), profile);
return true;
}
bool UserSessionManager::RestartToApplyPerSessionFlagsIfNeed(
Profile* profile,
bool early_restart) {
if (!ProfileHelper::IsUserProfile(profile)) {
return false;
}
if (ash::HibernateManager::IsHibernateSupported()) {
// No need to do anything if Hibernate isn't even supported on this
// device.
flags_ui::PrefServiceFlagsStorage flags_storage(profile->GetPrefs());
ash::HibernateManager::Get()->MaybeResume(flags_storage.GetFlags());
}
if (!SessionManagerClient::Get()->SupportsBrowserRestart()) {
return false;
}
MaybeSaveSessionStartedTimeBeforeRestart(profile);
// Kiosk sessions keeps the startup flags.
if (user_manager::UserManager::Get() &&
user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) {
return false;
}
if (early_restart && !CanPerformEarlyRestart())
return false;
// We can't really restart if we've already restarted as a part of
// user session restore after crash of in case when flags were changed inside
// user session.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kLoginUser))
return false;
// Don't restart browser if it is not the first profile in the session.
if (user_manager::UserManager::Get()->GetLoggedInUsers().size() != 1)
return false;
// Compare feature flags configured for the device vs. user. Restart is only
// required when there's a difference.
flags_ui::PrefServiceFlagsStorage flags_storage(profile->GetPrefs());
about_flags::FeatureFlagsUpdate update(flags_storage, profile->GetPrefs());
std::set<std::string> flags_difference;
if (!update.DiffersFromCommandLine(base::CommandLine::ForCurrentProcess(),
&flags_difference)) {
return false;
}
// Restart is required. Emit metrics and logs and trigger the restart.
LogCustomFeatureFlags(flags_difference);
LOG(WARNING) << "Restarting to apply per-session flags...";
update.UpdateSessionManager();
attempt_restart_closure_.Run();
return true;
}
void UserSessionManager::AddSessionStateObserver(
ash::UserSessionStateObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
session_state_observer_list_.AddObserver(observer);
}
void UserSessionManager::RemoveSessionStateObserver(
ash::UserSessionStateObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
session_state_observer_list_.RemoveObserver(observer);
}
void UserSessionManager::AddUserAuthenticatorObserver(
UserAuthenticatorObserver* observer) {
authenticator_observer_list_.AddObserver(observer);
}
void UserSessionManager::RemoveUserAuthenticatorObserver(
UserAuthenticatorObserver* observer) {
authenticator_observer_list_.RemoveObserver(observer);
}
void UserSessionManager::OnSessionRestoreStateChanged(
Profile* user_profile,
OAuth2LoginManager::SessionRestoreState state) {
user_manager::User::OAuthTokenStatus user_status =
user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN;
OAuth2LoginManager* login_manager =
OAuth2LoginManagerFactory::GetInstance()->GetForProfile(user_profile);
bool connection_error = false;
signin::IdentityManager* const identity_manager =
IdentityManagerFactory::GetForProfile(user_profile);
switch (state) {
case OAuth2LoginManager::SESSION_RESTORE_DONE:
if (identity_manager) {
// SESSION_RESTORE_DONE state means that primary account has a valid
// token.
DCHECK(
!identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
identity_manager->GetPrimaryAccountInfo(ConsentLevel::kSignin)
.account_id));
}
user_status = user_manager::User::OAUTH2_TOKEN_STATUS_VALID;
break;
case OAuth2LoginManager::SESSION_RESTORE_FAILED:
user_status = user_manager::User::OAUTH2_TOKEN_STATUS_INVALID;
break;
case OAuth2LoginManager::SESSION_RESTORE_CONNECTION_FAILED:
connection_error = true;
break;
case OAuth2LoginManager::SESSION_RESTORE_NOT_STARTED:
case OAuth2LoginManager::SESSION_RESTORE_PREPARING:
case OAuth2LoginManager::SESSION_RESTORE_IN_PROGRESS:
return;
}
// We should not be clearing existing token state if that was a connection
// error. http://crbug.com/295245
if (!connection_error) {
// We are in one of "done" states here.
user_manager::UserManager::Get()->SaveUserOAuthStatus(
user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(),
user_status);
}
login_manager->RemoveObserver(this);
// Terminate user session if merge session fails for an online sign-in.
// Otherwise, auth token dependent code would be in an invalid state.
// Important piece such as policy code might be broken because of this and
// subject to an exploit. See http://crbug.com/677312.
if (IsOnlineSignin(user_context_) &&
state == OAuth2LoginManager::SESSION_RESTORE_FAILED) {
SYSLOG(ERROR)
<< "Session restore failed for online sign-in, terminating session.";
chrome::AttemptUserExit();
return;
}
// Schedule another flush after session restore for non-ephemeral profile
// if not restarting.
if (!ProfileHelper::IsEphemeralUserProfile(user_profile)) {
BrowserContextFlusher::Get()->ScheduleFlush(user_profile);
}
}
void UserSessionManager::OnConnectionChanged(
network::mojom::ConnectionType type) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (type == network::mojom::ConnectionType::CONNECTION_NONE ||
!user_manager->IsUserLoggedIn() ||
!user_manager->IsLoggedInAsUserWithGaiaAccount() ||
user_manager->IsLoggedInAsStub() || IsRunningTest()) {
return;
}
// Need to iterate over all users and their OAuth2 session state.
for (const user_manager::User* user : user_manager->GetLoggedInUsers()) {
if (!user->is_profile_created())
continue;
Profile* user_profile = ProfileHelper::Get()->GetProfileByUser(user);
DCHECK(user_profile);
OAuth2LoginManager* login_manager =
OAuth2LoginManagerFactory::GetInstance()->GetForProfile(user_profile);
if (login_manager->SessionRestoreIsRunning()) {
// If we come online for the first time after successful offline login,
// we need to kick off OAuth token verification process again.
login_manager->ContinueSessionRestore();
} else if (pending_signin_restore_sessions_.erase(user->GetAccountId()) >
0) {
// Restore it, if the account is contained in the pending set.
RestoreAuthSessionImpl(user_profile, false /* has_auth_cookies */);
}
}
}
void UserSessionManager::OnProfilePrepared(Profile* profile,
bool browser_launched) {
if (!IsRunningTest()) {
// Did not log in (we crashed or are debugging), need to restore Sync.
// TODO(nkostylev): Make sure that OAuth state is restored correctly for all
// users once it is fully multi-profile aware. http://crbug.com/238987
// For now if we have other user pending sessions they'll override OAuth
// session restore for previous users.
RestoreAuthenticationSession(profile);
}
// Restore other user sessions if any.
RestorePendingUserSessions();
}
base::WeakPtr<UserSessionManagerDelegate> UserSessionManager::AsWeakPtr() {
return GetUserSessionManagerAsWeakPtr();
}
void UserSessionManager::OnUsersSignInConstraintsChanged() {
const user_manager::UserManager* user_manager =
user_manager::UserManager::Get();
const user_manager::UserList& logged_in_users =
user_manager->GetLoggedInUsers();
for (user_manager::User* user : logged_in_users) {
if (user->GetType() != user_manager::USER_TYPE_REGULAR &&
user->GetType() != user_manager::USER_TYPE_GUEST &&
user->GetType() != user_manager::USER_TYPE_CHILD) {
continue;
}
if (!user_manager->IsUserAllowed(*user)) {
SYSLOG(ERROR)
<< "The current user is not allowed, terminating the session.";
chrome::AttemptUserExit();
}
}
}
void UserSessionManager::ChildAccountStatusReceivedCallback(Profile* profile) {
StopChildStatusObserving(profile);
}
void UserSessionManager::StopChildStatusObserving(Profile* profile) {
if (waiting_for_child_account_status_ &&
!SessionStartupPref::TypeIsManaged(profile->GetPrefs())) {
MaybeLaunchHelpApp(profile);
}
waiting_for_child_account_status_ = false;
}
void UserSessionManager::CreateUserSession(const UserContext& user_context,
bool has_auth_cookies) {
user_context_ = user_context;
has_auth_cookies_ = has_auth_cookies;
ProcessAppModeSwitches();
StoreUserContextDataBeforeProfileIsCreated();
session_manager::SessionManager::Get()->CreateSession(
user_context_.GetAccountId(), user_context_.GetUserIDHash(),
user_context.GetUserType() == user_manager::USER_TYPE_CHILD);
}
void UserSessionManager::PreStartSession(StartSessionType start_session_type) {
// Switch log file as soon as possible.
RedirectChromeLogging(*base::CommandLine::ForCurrentProcess());
UserSessionInitializer::Get()->PreStartSession(start_session_type ==
StartSessionType::kPrimary);
}
void UserSessionManager::StoreUserContextDataBeforeProfileIsCreated() {
if (!user_context_.GetRefreshToken().empty()) {
// UserSelectionScreen::ShouldForceOnlineSignIn would force online sign-in
// if the token status isn't marked as valid.
user_manager::UserManager::Get()->SaveUserOAuthStatus(
user_context_.GetAccountId(),
user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
}
user_manager::KnownUser known_user(g_browser_process->local_state());
known_user.UpdateId(user_context_.GetAccountId());
}
void UserSessionManager::StartCrosSession() {
TRACE_EVENT0(kEventCategoryChromeOS, kEventStartCrosSession);
BootTimesRecorder* btl = BootTimesRecorder::Get();
btl->AddLoginTimeMarker("StartSession-Start", false);
if (base::FeatureList::IsEnabled(ownership::kChromeSideOwnerKeyGeneration)) {
SessionManagerClient::Get()->StartSessionEx(
cryptohome::CreateAccountIdentifierFromAccountId(
user_context_.GetAccountId()),
true);
} else {
SessionManagerClient::Get()->StartSession(
cryptohome::CreateAccountIdentifierFromAccountId(
user_context_.GetAccountId()));
}
btl->AddLoginTimeMarker("StartSession-End", false);
}
void UserSessionManager::VoteForSavingLoginPassword(
PasswordConsumingService service,
bool save_password) {
DCHECK_LT(service, PasswordConsumingService::kCount);
// VoteForSavingLoginPassword should only be called for the primary user
// session. It also should not be called when restarting the browser after a
// crash, because in that case a password is not available to chrome anymore.
if (start_session_type_ != StartSessionType::kPrimary) {
DLOG(WARNING)
<< "VoteForSavingPassword called for non-primary user session.";
return;
}
VLOG(1) << "Password consuming service " << static_cast<size_t>(service)
<< " votes " << save_password;
if (service == PasswordConsumingService::kNetwork) {
// When the network management code voted to either save or not save the
// login password for the primary user session, it is safe to load shill
// profile. Note that it is OK to invoke this multiple times, the upstart
// task triggered by this can handle it. This could happen if chrome has
// been restarted (e.g. due to a crash) within an active Chrome OS user
// session.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&UserSessionManager::LoadShillProfile,
GetUserSessionManagerAsWeakPtr(),
user_context_.GetAccountId()));
}
// Prevent this code from being called twice from two services or else the
// second service would trigger the warning below (since the password has been
// cleared).
if (save_password && !password_was_saved_) {
password_was_saved_ = true;
const std::string& password = user_context_.GetPasswordKey()->GetSecret();
if (!password.empty()) {
VLOG(1) << "Saving login password";
SessionManagerClient::Get()->SaveLoginPassword(password);
} else {
LOG(WARNING) << "Not saving password because password is empty.";
}
}
// If we've already sent the password or if all services voted 'no', forget
// the password again, it's not needed anymore.
password_service_voted_.set(static_cast<size_t>(service), true);
if (save_password || password_service_voted_.all()) {
VLOG(1) << "Clearing login password";
user_context_.GetMutablePasswordKey()->ClearSecret();
}
}
void UserSessionManager::InitDemoSessionIfNeeded(base::OnceClosure callback) {
DemoSession* demo_session = DemoSession::StartIfInDemoMode();
if (!demo_session || !demo_session->started()) {
std::move(callback).Run();
return;
}
should_launch_browser_ = false;
demo_session->EnsureResourcesLoaded(std::move(callback));
}
void UserSessionManager::UpdateArcFileSystemCompatibilityAndPrepareProfile() {
arc::UpdateArcFileSystemCompatibilityPrefIfNeeded(
user_context_.GetAccountId(),
ProfileHelper::GetProfilePathByUserIdHash(user_context_.GetUserIDHash()),
base::BindOnce(&UserSessionManager::InitializeAccountManager,
GetUserSessionManagerAsWeakPtr()));
}
void UserSessionManager::InitializeAccountManager() {
base::FilePath profile_path =
ProfileHelper::GetProfilePathByUserIdHash(user_context_.GetUserIDHash());
if (ProfileHelper::IsUserProfilePath(profile_path)) {
ash::InitializeAccountManager(
profile_path,
base::BindOnce(&UserSessionManager::PrepareProfile,
GetUserSessionManagerAsWeakPtr(),
profile_path) /* initialization_callback */);
} else {
PrepareProfile(profile_path);
}
}
void UserSessionManager::PrepareProfile(const base::FilePath& profile_path) {
TRACE_EVENT_NESTABLE_ASYNC_END0(
kEventCategoryChromeOS, kEventPrePrepareProfile, TRACE_ID_LOCAL(this));
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kEventCategoryChromeOS,
kEventPrepareProfile, TRACE_ID_LOCAL(this));
g_browser_process->profile_manager()->CreateProfileAsync(
profile_path,
/*initialized_callback=*/
base::BindOnce(
[](base::WeakPtr<UserSessionManager> self,
const UserContext& user_context, Profile* profile) {
// `profile` might be null, meaning that the creation failed.
if (!profile || !self) {
return;
}
// Profile is created, extensions and promo resources
// are initialized. At this point all other Chrome OS
// services will be notified that it is safe to use
// this profile.
self->UserProfileInitialized(profile, user_context.GetAccountId());
},
GetUserSessionManagerAsWeakPtr(), user_context_),
/*created_callback=*/
base::BindOnce(
[](base::WeakPtr<UserSessionManager> self,
const UserContext& user_context, Profile* profile) {
// `profile` might be null, meaning that the creation failed.
if (!profile || !self) {
return;
}
// Profile created but before initializing extensions and
// promo resources.
self->InitProfilePreferences(profile, user_context);
},
GetUserSessionManagerAsWeakPtr(), user_context_));
}
void UserSessionManager::InitProfilePreferences(
Profile* profile,
const UserContext& user_context) {
DVLOG(1) << "Initializing profile preferences";
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(profile);
if (user->GetType() == user_manager::USER_TYPE_KIOSK_APP &&
profile->IsNewProfile()) {
ChromeUserManager::Get()->SetIsCurrentUserNew(true);
}
if (user->is_active()) {
auto* manager = input_method::InputMethodManager::Get();
manager->SetState(GetDefaultIMEState(profile));
}
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
// Set initial prefs if the user is new, or if the user was already present on
// the device and the profile was re-created. This can happen e.g. in ext4
// migration in wipe mode.
const bool is_new_profile = IsNewProfile(profile);
if (is_new_profile) {
SetFirstLoginPrefs(profile, user_context.GetPublicSessionLocale(),
user_context.GetPublicSessionInputMethod());
}
std::optional<base::Version> onboarding_completed_version =
user_manager::KnownUser(g_browser_process->local_state())
.GetOnboardingCompletedVersion(user->GetAccountId());
if (!onboarding_completed_version.has_value()) {
// Device local accounts do not have onboarding.
if (LoginDisplayHost::default_host() &&
LoginDisplayHost::default_host()->GetSigninUI() &&
user_manager->GetPrimaryUser() == user &&
!user_manager->IsUserNonCryptohomeDataEphemeral(user->GetAccountId()) &&
!user->IsDeviceLocalAccount()) {
LoginDisplayHost::default_host()
->GetSigninUI()
->SetAuthSessionForOnboarding(user_context);
}
}
if (user_manager->IsLoggedInAsUserWithGaiaAccount()) {
// Get the Gaia ID from the user context. This may not be available when
// unlocking a previously opened profile, or when creating a supervised
// user. However, in these cases the gaia_id should be already available in
// `IdentityManager`.
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
std::string gaia_id = user_context.GetGaiaID();
// TODO(http://crbug.com/1454286): Remove.
bool used_extended_account_info = false;
if (gaia_id.empty()) {
const AccountInfo account_info =
identity_manager->FindExtendedAccountInfoByEmailAddress(
user_context.GetAccountId().GetUserEmail());
DCHECK(!account_info.IsEmpty() || IsRunningTest());
gaia_id = account_info.gaia;
used_extended_account_info = true;
// Use a fake gaia id for tests that do not have it.
if (IsRunningTest() && gaia_id.empty())
gaia_id = "fake_gaia_id_" + user_context.GetAccountId().GetUserEmail();
// Update http://crbug.com/1454286 if the following line CHECKs.
CHECK(!gaia_id.empty());
}
// Set the Primary Account. Since `IdentityManager` requires that the
// account is seeded before it can be set as primary, there are three main
// steps in this process:
// 1. Make sure that the Primary Account is present in
// `account_manager::AccountManager`.
// 2. Seed it into `IdentityManager`.
// 3. Set it as the Primary Account.
account_manager::AccountManager* account_manager =
g_browser_process->platform_part()
->GetAccountManagerFactory()
->GetAccountManager(profile->GetPath().value());
DCHECK(account_manager->IsInitialized());
const ::account_manager::AccountKey account_key{
gaia_id, account_manager::AccountType::kGaia};
// 1. Make sure that the account is present in
// `account_manager::AccountManager`.
if (!user_context.GetRefreshToken().empty()) {
// `account_manager::AccountManager::UpsertAccount` is idempotent. We can
// safely call it without checking for re-auth cases. We MUST NOT revoke
// old Device Account tokens (`revoke_old_token` = `false`), otherwise
// Gaia will revoke all tokens associated to this user's device id,
// including `refresh_token_` and the user will be stuck performing an
// online auth with Gaia at every login. See https://crbug.com/952570 and
// https://crbug.com/865189 for context.
account_manager->UpsertAccount(account_key,
user->GetDisplayEmail() /* raw_email */,
user_context.GetRefreshToken());
} else if (!account_manager->IsTokenAvailable(account_key)) {
// When `user_context` does not contain a refresh token and account is
// not present in the AccountManager it means the migration to the
// AccountManager didn't happen.
// Set account with dummy token to let IdentitManager know that account
// exists and we can safely configure the primary account at the step 2.
// The real token will be set later during the migration.
account_manager->UpsertAccount(
account_key, user->GetDisplayEmail() /* raw_email */,
account_manager::AccountManager::kInvalidToken);
}
DCHECK(account_manager->IsTokenAvailable(account_key));
// 2. Seed it into `IdentityManager`.
// TODO(https://crbug.com/1196784): Check whether we should use
// GetAccountId().GetUserEmail() instead of GetDisplayEmail() here.
signin::AccountsMutator* accounts_mutator =
identity_manager->GetAccountsMutator();
CoreAccountId account_id =
accounts_mutator->SeedAccountInfo(gaia_id, user->GetDisplayEmail());
// 3. Set it as the Primary Account.
const signin::PrimaryAccountMutator::PrimaryAccountError
set_account_result =
identity_manager->GetPrimaryAccountMutator()->SetPrimaryAccount(
account_id, ConsentLevel::kSync);
VLOG(1) << "SetPrimaryAccount result="
<< static_cast<int>(set_account_result);
// TODO(http://crbug.com/1454286): Remove.
const CoreAccountInfo& identity_manager_account_info =
identity_manager->GetPrimaryAccountInfo(ConsentLevel::kSync);
if (identity_manager_account_info.gaia != gaia_id) {
signin::PrimaryAccountMutator::PrimaryAccountError
set_account_result_copy = set_account_result;
bool used_extended_account_info_copy = used_extended_account_info;
base::debug::Alias(&set_account_result_copy);
base::debug::Alias(&used_extended_account_info_copy);
DEBUG_ALIAS_FOR_CSTR(local_gaia_id_str, gaia_id.c_str(), 32);
DEBUG_ALIAS_FOR_CSTR(identity_manager_gaia_id_str,
identity_manager_account_info.gaia.c_str(), 32);
DEBUG_ALIAS_FOR_CSTR(
account_id_str, user_context.GetAccountId().Serialize().c_str(), 128);
DEBUG_ALIAS_FOR_CSTR(core_account_id_str, account_id.ToString().c_str(),
128);
DEBUG_ALIAS_FOR_CSTR(identity_manager_email_str,
identity_manager_account_info.email.c_str(), 32);
DEBUG_ALIAS_FOR_CSTR(
identity_manager_id_str,
identity_manager_account_info.account_id.ToString().c_str(), 32);
}
CHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSync));
CHECK_EQ(identity_manager->GetPrimaryAccountInfo(ConsentLevel::kSync).gaia,
gaia_id);
DCHECK_EQ(account_id,
identity_manager->GetPrimaryAccountId(ConsentLevel::kSignin));
VLOG(1) << "Seed IdentityManager with the authenticated account info, "
<< "success=" << !account_id.empty();
// At this point, the Device Account has been loaded. Start observing its
// refresh token for changes, so that token handle can be kept in sync.
if (TokenHandlesEnabled()) {
auto device_account_token_observer =
std::make_unique<DeviceAccountGaiaTokenObserver>(
account_manager, user->GetAccountId(),
base::BindRepeating(&UserSessionManager::UpdateTokenHandle,
GetUserSessionManagerAsWeakPtr(), profile));
auto it = token_observers_.find(profile);
if (it == token_observers_.end()) {
token_observers_.emplace(profile,
std::move(device_account_token_observer));
} else {
NOTREACHED()
<< "Found an existing Gaia token observer for this Profile. "
"Profile is being erroneously initialized twice?";
}
}
user = user_manager->FindUser(user_context.GetAccountId());
bool is_child = user->GetType() == user_manager::USER_TYPE_CHILD;
DCHECK(is_child ==
(user_context.GetUserType() == user_manager::USER_TYPE_CHILD));
signin::Tribool is_under_advanced_protection = signin::Tribool::kUnknown;
if (IsOnlineSignin(user_context)) {
is_under_advanced_protection = user_context.IsUnderAdvancedProtection()
? signin::Tribool::kTrue
: signin::Tribool::kFalse;
}
identity_manager->GetAccountsMutator()->UpdateAccountInfo(
account_id, is_child ? signin::Tribool::kTrue : signin::Tribool::kFalse,
is_under_advanced_protection);
if (is_child &&
base::FeatureList::IsEnabled(::features::kDMServerOAuthForChildUser)) {
child_policy_observer_ = std::make_unique<ChildPolicyObserver>(profile);
}
} else {
// Active Directory (non-supervised, non-GAIA) accounts take this path.
}
}
void UserSessionManager::InitializeDeviceId(
bool is_ephemeral_user,
UserContext& user_context,
user_manager::KnownUser& known_user) {
const AccountId& account_id = user_context.GetAccountId();
// `UserContext` and `KnownUser` should have the same device id cached /
// stored in them. The source of truth is the value persisted in `KnownUser`
// so check that first.
const std::string stored_device_id = known_user.GetDeviceId(account_id);
if (!stored_device_id.empty()) {
user_context.SetDeviceId(stored_device_id);
return;
}
// We do not have any device id stored on disk. If `UserContext` already has
// a device id, respect that.
const std::string cached_device_id = user_context.GetDeviceId();
if (!cached_device_id.empty()) {
known_user.SetDeviceId(account_id, cached_device_id);
return;
}
if (!base::FeatureList::IsEnabled(kStableDeviceId)) {
// Do not generate and store new device identifiers if the feature is not
// enabled yet.
return;
}
// We do not have any device id - neither stored on disk, nor cached in
// memory. Generate and persist a new one.
const std::string device_id = GenerateSigninScopedDeviceId(is_ephemeral_user);
user_context.SetDeviceId(device_id);
known_user.SetDeviceId(account_id, device_id);
}
void UserSessionManager::UserProfileInitialized(Profile* profile,
const AccountId& account_id) {
TRACE_EVENT0("login", "UserSessionManager::UserProfileInitialized");
// Check whether this `profile` was already initialized.
if (user_profile_initialized_called_.contains(profile))
return;
user_profile_initialized_called_.insert(profile);
BootTimesRecorder* btl = BootTimesRecorder::Get();
btl->AddLoginTimeMarker("UserProfileGotten", false);
// Associates AppListClient with the current active profile.
// Make sure AppListClient is active when AppListSyncableService builds
// model to avoid oem folder being created with invalid position. Note we
// should put this call before OAuth check in case of gaia sign in.
AppListClientImpl::GetInstance()->UpdateProfile();
if (user_context_.IsUsingOAuth()) {
// Retrieve the policy that indicates whether to continue copying
// authentication cookies set by a SAML IdP on subsequent logins after the
// first.
bool transfer_saml_auth_cookies_on_subsequent_login = false;
if (has_auth_cookies_) {
const user_manager::User* user =
user_manager::UserManager::Get()->FindUser(account_id);
if (user->IsAffiliated()) {
CrosSettings::Get()->GetBoolean(
kAccountsPrefTransferSAMLCookies,
&transfer_saml_auth_cookies_on_subsequent_login);
}
}
const bool in_session_password_change_feature_enabled =
base::FeatureList::IsEnabled(::features::kInSessionPasswordChange);
if (in_session_password_change_feature_enabled &&
user_context_.GetSamlPasswordAttributes().has_value()) {
// Update password expiry data if new data came in during SAML login,
// and the in-session password change feature is enabled:
user_context_.GetSamlPasswordAttributes()->SaveToPrefs(
profile->GetPrefs());
} else if (!in_session_password_change_feature_enabled ||
user_context_.GetAuthFlow() ==
UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML) {
// These attributes are no longer relevant and should be deleted if
// either a) the in-session password change feature is no longer enabled
// or b) this user is no longer using SAML to log in.
SamlPasswordAttributes::DeleteFromPrefs(profile->GetPrefs());
}
// Transfers authentication-related data from the profile that was used
// for authentication to the user's profile. The proxy authentication
// state is transferred unconditionally. If the user authenticated via an
// auth extension, authentication cookies will be transferred as well when
// the user's cookie jar is empty. If the cookie jar is not empty, the
// authentication states in the browser context and the user's profile
// must be merged using /MergeSession instead. Authentication cookies set
// by a SAML IdP will also be transferred when the user's cookie jar is
// not empty if `transfer_saml_auth_cookies_on_subsequent_login` is true.
const bool transfer_auth_cookies_on_first_login = has_auth_cookies_;
content::StoragePartition* signin_partition = login::GetSigninPartition();
// Authentication request context may be missing especially if user didn't
// sign in using GAIA (webview) and webview didn't yet initialize.
if (signin_partition) {
ProfileAuthData::Transfer(
signin_partition, profile->GetDefaultStoragePartition(),
transfer_auth_cookies_on_first_login,
transfer_saml_auth_cookies_on_subsequent_login,
base::BindOnce(
&UserSessionManager::CompleteProfileCreateAfterAuthTransfer,
GetUserSessionManagerAsWeakPtr(), profile));
} else {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(
&UserSessionManager::CompleteProfileCreateAfterAuthTransfer,
GetUserSessionManagerAsWeakPtr(), profile));
}
return;
}
BootTimesRecorder::Get()->AddLoginTimeMarker("TPMOwn-Start", false);
PrepareTpm(base::BindOnce(OnPrepareTpmDeviceFinished));
FinalizePrepareProfile(profile);
}
void UserSessionManager::CompleteProfileCreateAfterAuthTransfer(
Profile* profile) {
TRACE_EVENT0("login",
"UserSessionManager::CompleteProfileCreateAfterAuthTransfer");
RestoreAuthSessionImpl(profile, has_auth_cookies_);
BootTimesRecorder::Get()->AddLoginTimeMarker("TPMOwn-Start", false);
PrepareTpm(base::BindOnce(OnPrepareTpmDeviceFinished));
FinalizePrepareProfile(profile);
}
void UserSessionManager::FinalizePrepareProfile(Profile* profile) {
TRACE_EVENT0("login", "UserSessionManager::FinalizePrepareProfile");
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
user_manager::KnownUser known_user(g_browser_process->local_state());
if (user_manager->IsLoggedInAsUserWithGaiaAccount()) {
const UserContext::AuthFlow auth_flow = user_context_.GetAuthFlow();
if (auth_flow == UserContext::AUTH_FLOW_GAIA_WITH_SAML ||
auth_flow == UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML) {
const bool using_saml =
auth_flow == UserContext::AUTH_FLOW_GAIA_WITH_SAML;
const AccountId& account_id = user_context_.GetAccountId();
known_user.UpdateUsingSAML(account_id, using_saml);
known_user.UpdateIsUsingSAMLPrincipalsAPI(
account_id,
using_saml ? user_context_.IsUsingSamlPrincipalsApi() : false);
user->set_using_saml(using_saml);
if (!using_saml) {
known_user.ClearPasswordSyncToken(account_id);
}
}
PasswordSyncTokenVerifier* password_sync_token_verifier =
PasswordSyncTokenVerifierFactory::GetForProfile(profile);
// PasswordSyncTokenVerifier can be created only for SAML users.
if (password_sync_token_verifier) {
CHECK(user->using_saml());
if (auth_flow == UserContext::AUTH_FLOW_GAIA_WITH_SAML) {
// Update local sync token after online SAML login.
password_sync_token_verifier->FetchSyncTokenOnReauth();
} else if (auth_flow == UserContext::AUTH_FLOW_OFFLINE) {
// Verify local sync token to check whether the local password is out
// of sync.
password_sync_token_verifier->RecordTokenPollingStart();
password_sync_token_verifier->CheckForPasswordNotInSync();
} else {
// SAML user is not expected to go through other authentication flows.
NOTREACHED();
}
}
OfflineSigninLimiter* offline_signin_limiter =
OfflineSigninLimiterFactory::GetForProfile(profile);
if (offline_signin_limiter)
offline_signin_limiter->SignedIn(auth_flow);
}
profile->OnLogin();
TRACE_EVENT_NESTABLE_ASYNC_END0(kEventCategoryChromeOS, kEventPrepareProfile,
TRACE_ID_LOCAL(this));
{
TRACE_EVENT0(kEventCategoryChromeOS, kEventHandleProfileLoad);
NotifyUserProfileLoaded(profile, user);
// Initialize various services only for primary user.
if (user_manager->GetPrimaryUser() == user) {
StartTetherServiceIfPossible(profile);
ash::AlwaysOnVpnPreConnectUrlAllowlistService* service =
ash::AlwaysOnVpnPreConnectUrlAllowlistServiceFactory::GetInstance()
->GetForProfile(profile);
// PrefService is ready, check whether we need to force a VPN connection.
always_on_vpn_manager_ = std::make_unique<arc::AlwaysOnVpnManager>(
profile->GetPrefs(),
/*delay_lockdown_until_vpn_connected=*/service &&
service->enforce_alwayson_pre_connect_url_allowlist());
if (service) {
// Configure the `AlwaysOnVpnPreConnectUrlAllowlistService` service to
// store a weak ptr reference to `always_on_vpn_manager_` because it
// outlives the `always_on_vpn_manager_` instance.
service->SetAlwaysOnVpnManager(always_on_vpn_manager_->GetWeakPtr());
}
secure_dns_manager_ =
std::make_unique<SecureDnsManager>(g_browser_process->local_state());
xdr_manager_ =
std::make_unique<XdrManager>(g_browser_process->policy_service());
}
// Save sync password hash and salt to profile prefs if they are available.
// These will be used to detect Gaia password reuses.
if (user_context_.GetSyncPasswordData().has_value()) {
login::SaveSyncPasswordDataToProfile(user_context_, profile);
}
if (!user_context_.GetChallengeResponseKeys().empty()) {
PersistChallengeResponseKeys(user_context_);
login::SecurityTokenSessionControllerFactory::GetForBrowserContext(
profile)
->OnChallengeResponseKeysUpdated();
login::SecurityTokenSessionControllerFactory::GetForBrowserContext(
ProfileHelper::GetSigninProfile())
->OnChallengeResponseKeysUpdated();
}
if (user_context_.GetSyncTrustedVaultKeys().has_value()) {
SaveSyncTrustedVaultKeysToProfile(
user_context_.GetGaiaID(), *user_context_.GetSyncTrustedVaultKeys(),
profile);
}
VLOG(1) << "Clearing all secrets";
user_context_.ClearSecrets();
if (user->GetType() == user_manager::USER_TYPE_CHILD) {
if (base::FeatureList::IsEnabled(
::features::kDMServerOAuthForChildUser)) {
VLOG(1) << "Waiting for child policy refresh before showing session UI";
DCHECK(child_policy_observer_);
child_policy_observer_->NotifyWhenPolicyReady(
base::BindOnce(&UserSessionManager::OnChildPolicyReady,
GetUserSessionManagerAsWeakPtr()),
kWaitForChildPolicyTimeout);
return;
}
}
}
InitializeBrowser(profile);
}
void UserSessionManager::InitializeBrowser(Profile* profile) {
// Now that profile is ready, proceed to either alternative login flows or
// launch browser.
bool browser_launched = InitializeUserSession(profile);
// Only allow Quirks downloads after login is finished.
quirks::QuirksManager::Get()->OnLoginCompleted();
// Schedule a flush if profile is not ephemeral.
if (!ProfileHelper::IsEphemeralUserProfile(profile)) {
BrowserContextFlusher::Get()->ScheduleFlush(profile);
}
// TODO(nkostylev): This pointer should probably never be NULL, but it looks
// like CreateProfileAsync callback may be getting called before
// UserSessionManager::PrepareProfile() has set `delegate_` when Chrome is
// killed during shutdown in tests -- see http://crosbug.com/18269. Replace
// this 'if' statement with a CHECK(delegate_) once the underlying issue is
// resolved.
if (delegate_)
delegate_->OnProfilePrepared(profile, browser_launched);
if (ProfileHelper::IsPrimaryProfile(profile) &&
OnboardingUserActivityCounter::ShouldStart(profile->GetPrefs())) {
onboarding_user_activity_counter_ =
std::make_unique<OnboardingUserActivityCounter>(
profile->GetPrefs(), GetActivityTimeBeforeOnboardingSurvey(),
base::BindOnce(
&UserSessionManager::OnUserEligibleForOnboardingSurvey,
GetUserSessionManagerAsWeakPtr(), profile));
}
}
void UserSessionManager::MaybeLaunchHelpApp(Profile* profile) const {
if (first_run::ShouldLaunchHelpApp(profile)) {
// Don't open default Chrome window if we're going to launch the first-run
// app. Because we don't want the first-run app to be hidden in the
// background.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kSilentLaunch);
first_run::LaunchHelpApp(profile);
}
}
bool UserSessionManager::MaybeStartNewUserOnboarding(Profile* profile) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (!user_manager->IsCurrentUserNew()) {
return false;
}
supervised_user::ChildAccountService* child_service =
ChildAccountServiceFactory::GetForProfile(profile);
child_service->AddChildStatusReceivedCallback(
base::BindOnce(&UserSessionManager::ChildAccountStatusReceivedCallback,
GetUserSessionManagerAsWeakPtr(), profile));
PrefService* prefs = profile->GetPrefs();
prefs->SetTime(prefs::kOobeOnboardingTime, base::Time::Now());
prefs->SetBoolean(arc::prefs::kArcPlayStoreLaunchMetricCanBeRecorded, true);
// Don't specify start URLs if the administrator has configured the
// start URLs via policy.
if (!SessionStartupPref::TypeIsManaged(prefs)) {
if (child_service->IsChildAccountStatusKnown())
MaybeLaunchHelpApp(profile);
else
waiting_for_child_account_status_ = true;
}
// Mark the device as registered., i.e. the second part of OOBE as
// completed.
if (!StartupUtils::IsDeviceRegistered())
StartupUtils::MarkDeviceRegistered(base::OnceClosure());
if (LoginDisplayHost::default_host() &&
LoginDisplayHost::default_host()->GetSigninUI()) {
LoginDisplayHost::default_host()->GetSigninUI()->StartUserOnboarding();
} else {
LOG(WARNING) << "Can't start user onboarding as LoginDisplayHost has been "
"already destroyed!";
}
OnboardingUserActivityCounter::MaybeMarkForStart(profile);
return true;
}
bool MaybeResumeUserOnboardingFlow(Profile* profile) {
const AccountId account_id =
ProfileHelper::Get()->GetUserByProfile(profile)->GetAccountId();
user_manager::KnownUser known_user(g_browser_process->local_state());
std::string pending_screen =
known_user.GetPendingOnboardingScreen(account_id);
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->IsCurrentUserNew() || pending_screen.empty()) {
return false;
}
if (LoginDisplayHost::default_host() &&
LoginDisplayHost::default_host()->GetSigninUI()) {
LoginDisplayHost::default_host()->GetSigninUI()->ResumeUserOnboarding(
*profile->GetPrefs(), OobeScreenId(pending_screen));
} else {
LOG(WARNING) << "Can't resume onboarding as LoginDisplayHost has been "
"already destroyed!";
}
return true;
}
bool MaybeStartManagementTransition(Profile* profile) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->IsCurrentUserNew() ||
arc::GetManagementTransition(profile) ==
arc::ArcManagementTransition::NO_TRANSITION) {
return false;
}
if (LoginDisplayHost::default_host() &&
LoginDisplayHost::default_host()->GetSigninUI()) {
LoginDisplayHost::default_host()
->GetSigninUI()
->StartManagementTransition();
} else {
LOG(WARNING) << "Can't start management transition as LoginDisplayHost has "
"been already destroyed!";
}
return true;
}
bool MaybeShowManagedTermsOfService(Profile* profile) {
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->IsCurrentUserNew() ||
!profile->GetPrefs()->IsManagedPreference(::prefs::kTermsOfServiceURL)) {
return false;
}
if (LoginDisplayHost::default_host() &&
LoginDisplayHost::default_host()->GetSigninUI()) {
LoginDisplayHost::default_host()->GetSigninUI()->ShowTosForExistingUser();
} else {
LOG(WARNING) << "Can't show additional terms of service as "
"LoginDisplayHost has been already destroyed!";
}
return true;
}
bool UserSessionManager::InitializeUserSession(Profile* profile) {
TRACE_EVENT0(kEventCategoryChromeOS, kEventInitUserDesktop);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&UserSessionManager::StopChildStatusObserving,
GetUserSessionManagerAsWeakPtr(), profile),
kFlagsFetchingLoginTimeout);
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
// Kiosk apps has their own session initialization pipeline.
if (user_manager->IsLoggedInAsAnyKioskApp()) {
return false;
}
SigninProfileHandler::Get()->ProfileStartUp(profile);
PrefService* prefs = profile->GetPrefs();
arc::RecordPlayStoreLaunchWithinAWeek(prefs, /*launched=*/false);
if (start_session_type_ == StartSessionType::kPrimary) {
user_manager::KnownUser known_user(g_browser_process->local_state());
const AccountId account_id =
ProfileHelper::Get()->GetUserByProfile(profile)->GetAccountId();
std::string pending_screen =
known_user.GetPendingOnboardingScreen(account_id);
if (!pending_screen.empty() &&
!WizardController::IsResumablePostLoginScreen(
OobeScreenId(pending_screen))) {
pending_screen.clear();
known_user.RemovePendingOnboardingScreen(account_id);
}
std::optional<base::Version> onboarding_completed_version =
known_user.GetOnboardingCompletedVersion(account_id);
if (!user_manager->IsCurrentUserNew() && pending_screen.empty() &&
!onboarding_completed_version.has_value()) {
known_user.SetOnboardingCompletedVersion(
account_id, base::Version(kOnboardingBackfillVersion));
if (LoginDisplayHost::default_host() &&
LoginDisplayHost::default_host()->GetSigninUI()) {
LoginDisplayHost::default_host()
->GetSigninUI()
->ClearOnboardingAuthSession();
} else {
LOG(WARNING) << "Can't clear onboarding auth session as "
"LoginDisplayHost has been already destroyed!";
}
}
if (MaybeStartNewUserOnboarding(profile)) {
return false;
}
if (MaybeShowNewTermsAfterUpdateToFlex(profile)) {
return false;
}
if (MaybeResumeUserOnboardingFlow(profile)) {
return false;
}
if (MaybeStartManagementTransition(profile)) {
return false;
}
if (MaybeShowManagedTermsOfService(profile)) {
return false;
}
}
DoBrowserLaunch(profile);
return true;
}
void UserSessionManager::ProcessAppModeSwitches() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
bool in_app_mode = chrome::IsRunningInForcedAppMode();
// Are we in kiosk app mode?
if (in_app_mode) {
if (command_line->HasSwitch(::switches::kAppModeOAuth2Token)) {
user_context_.SetRefreshToken(
command_line->GetSwitchValueASCII(::switches::kAppModeOAuth2Token));
}
if (command_line->HasSwitch(::switches::kAppModeAuthCode)) {
user_context_.SetAuthCode(
command_line->GetSwitchValueASCII(::switches::kAppModeAuthCode));
}
DCHECK(!has_auth_cookies_);
}
}
void UserSessionManager::RestoreAuthSessionImpl(
Profile* profile,
bool restore_from_auth_cookies) {
CHECK(authenticator_.get() || !restore_from_auth_cookies);
if (chrome::IsRunningInForcedAppMode() ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableGaiaServices)) {
return;
}
OAuth2LoginManager* login_manager =
OAuth2LoginManagerFactory::GetInstance()->GetForProfile(profile);
login_manager->AddObserver(this);
login_manager->RestoreSession(user_context_.GetAccessToken());
}
void UserSessionManager::NotifyUserProfileLoaded(
Profile* profile,
const user_manager::User* user) {
session_manager::SessionManager::Get()->NotifyUserProfileLoaded(
user->GetAccountId());
if (TokenHandlesEnabled() && user && user->HasGaiaAccount()) {
CreateTokenUtilIfMissing();
if (IsOnlineSignin(user_context_)) {
// If the user has gone through an online Gaia flow, then their LST is
// guaranteed to have changed/created. We need to update the token handle,
// regardless of the state of the previous token handle, if any.
if (!token_handle_util_->HasToken(user_context_.GetAccountId())) {
// New user.
token_handle_fetcher_ = std::make_unique<TokenHandleFetcher>(
profile, token_handle_util_.get(), user_context_.GetAccountId());
token_handle_fetcher_->FillForNewUser(
user_context_.GetAccessToken(),
Sha1Digest(user_context_.GetRefreshToken()),
base::BindOnce(&UserSessionManager::OnTokenHandleObtained,
GetUserSessionManagerAsWeakPtr()));
} else {
// Existing user.
UpdateTokenHandle(profile, user->GetAccountId());
}
} else {
UpdateTokenHandleIfRequired(profile, user->GetAccountId());
}
}
}
void UserSessionManager::StartTetherServiceIfPossible(Profile* profile) {
auto* tether_service = tether::TetherService::Get(profile);
if (tether_service)
tether_service->StartTetherIfPossible();
}
void UserSessionManager::ShowNotificationsIfNeeded(Profile* profile) {
// Checks whether to show the update notification.
MaybeShowUpdateNotification(profile);
// Check to see if this profile should show TPM Firmware Update Notification
// and show the message accordingly.
tpm_firmware_update::ShowNotificationIfNeeded(profile);
// Show legacy U2F notification if applicable.
MaybeShowU2FNotification();
// If the profile does not meet the criteria for showing the Discover
// notification, show the Release Notes notification early instead of waiting
// for the background page to trigger it.
if (!GetHelpAppNotificationController(profile)
->ShouldShowDiscoverNotification()) {
MaybeShowHelpAppReleaseNotesNotification(profile);
}
g_browser_process->platform_part()
->browser_policy_connector_ash()
->GetTPMAutoUpdateModePolicyHandler()
->ShowTPMAutoUpdateNotificationIfNeeded();
GetMinimumVersionPolicyHandler()->MaybeShowNotificationOnLogin();
// Show a notification about ADB sideloading policy change if applicable.
g_browser_process->platform_part()
->browser_policy_connector_ash()
->GetAdbSideloadingAllowanceModePolicyHandler()
->ShowAdbSideloadingPolicyChangeNotificationIfNeeded();
}
void UserSessionManager::PerformPostBrowserLaunchOOBEActions(Profile* profile) {
SyncConsentScreen::MaybeLaunchSyncConsentSettings(profile);
if (features::IsOobeDisplaySizeEnabled()) {
DisplaySizeScreen::MaybeUpdateZoomFactor(profile);
}
if (drive::util::IsOobeDrivePinningAvailable(profile)) {
DrivePinningScreen::ApplyDrivePinningPref(profile);
}
}
void UserSessionManager::OnRestoreActiveSessions(
std::optional<SessionManagerClient::ActiveSessionsMap> sessions) {
if (!sessions.has_value()) {
LOG(ERROR) << "Could not get list of active user sessions after crash.";
// If we could not get list of active user sessions it is safer to just
// sign out so that we don't get in the inconsistent state.
SessionTerminationManager::Get()->StopSession(
login_manager::SessionStopReason::RESTORE_ACTIVE_SESSIONS);
return;
}
// One profile has been already loaded on browser start.
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
DCHECK_EQ(1u, user_manager->GetLoggedInUsers().size());
DCHECK(user_manager->GetActiveUser());
const cryptohome::Identification active_cryptohome_id(
user_manager->GetActiveUser()->GetAccountId());
user_manager::KnownUser known_user(g_browser_process->local_state());
for (auto& [cryptohome_id, user_id_hash] : sessions.value()) {
if (active_cryptohome_id.id() == cryptohome_id)
continue;
const AccountId account_id(known_user.GetAccountIdByCryptohomeId(
user_manager::CryptohomeId(cryptohome_id)));
pending_user_sessions_[account_id] = std::move(user_id_hash);
}
RestorePendingUserSessions();
}
void UserSessionManager::RestorePendingUserSessions() {
if (pending_user_sessions_.empty()) {
// '>1' ignores "restart on signin" because of browser flags difference.
// In this case, last_session_active_account_id_ can carry account_id
// from the previous browser session.
if (user_manager::UserManager::Get()->GetLoggedInUsers().size() > 1)
user_manager::UserManager::Get()->SwitchToLastActiveUser();
NotifyPendingUserSessionsRestoreFinished();
return;
}
// Get next user to restore sessions and delete it from list.
PendingUserSessions::const_iterator it = pending_user_sessions_.begin();
const AccountId account_id = it->first;
std::string user_id_hash = it->second;
DCHECK(account_id.is_valid());
DCHECK(!user_id_hash.empty());
pending_user_sessions_.erase(account_id);
// Check that this user is not logged in yet.
// TODO(alemate): Investigate whether this could be simplified by enforcing
// session restore to existing users only. Currently this breakes some tests
// (namely CrashRestoreComplexTest.RestoreSessionForThreeUsers), but
// it may be test-specific and could probably be changed.
const bool user_already_logged_in =
base::Contains(user_manager::UserManager::Get()->GetLoggedInUsers(),
account_id, &user_manager::User::GetAccountId);
DCHECK(!user_already_logged_in);
if (!user_already_logged_in) {
const user_manager::User* const user =
user_manager::UserManager::Get()->FindUser(account_id);
UserContext user_context =
user ? UserContext(*user)
: UserContext(user_manager::UserType::USER_TYPE_REGULAR,
account_id);
user_context.SetUserIDHash(user_id_hash);
user_context.SetIsUsingOAuth(false);
// Will call OnProfilePrepared() once profile has been loaded.
// Only handling secondary users here since primary user profile
// (and session) has been loaded on Chrome startup.
StartSession(user_context, StartSessionType::kSecondaryAfterCrash,
false, // has_auth_cookies
true, // has_active_session, this is restart after crash
AsWeakPtr());
} else {
RestorePendingUserSessions();
}
}
void UserSessionManager::NotifyPendingUserSessionsRestoreFinished() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
user_sessions_restored_ = true;
user_sessions_restore_in_progress_ = false;
for (auto& observer : session_state_observer_list_)
observer.PendingUserSessionsRestoreFinished();
}
void UserSessionManager::OnChildPolicyReady(
Profile* profile,
ChildPolicyObserver::InitialPolicyRefreshResult result) {
VLOG(1) << "Child policy refresh finished with result "
<< static_cast<int>(result) << " - showing session UI";
DCHECK(profile->IsChild());
child_policy_observer_.reset();
UserSessionInitializer::Get()->InitializeChildUserServices(profile);
InitializeBrowser(profile);
}
void UserSessionManager::ActiveUserChanged(user_manager::User* active_user) {
Profile* profile = ProfileHelper::Get()->GetProfileByUser(active_user);
// If profile has not yet been initialized, delay initialization of IME.
if (!profile)
return;
auto* manager = input_method::InputMethodManager::Get();
// `manager` might not be available in some unit tests.
if (!manager)
return;
manager->SetState(
GetDefaultIMEState(ProfileHelper::Get()->GetProfileByUser(active_user)));
manager->MaybeNotifyImeMenuActivationChanged();
}
scoped_refptr<input_method::InputMethodManager::State>
UserSessionManager::GetDefaultIMEState(Profile* profile) {
scoped_refptr<input_method::InputMethodManager::State> state =
default_ime_states_[profile];
if (!state.get()) {
// Profile can be NULL in tests.
state = input_method::InputMethodManager::Get()->CreateNewState(profile);
if (ProfileHelper::Get()->IsSigninProfile(profile))
state->SetUIStyle(input_method::InputMethodManager::UIStyle::kLogin);
default_ime_states_[profile] = state;
}
return state;
}
void UserSessionManager::CheckEolInfo(Profile* profile) {
if (!EolNotification::ShouldShowEolNotification())
return;
std::map<Profile*, std::unique_ptr<EolNotification>, ProfileCompare>::iterator
iter = eol_notification_handler_.find(profile);
if (iter == eol_notification_handler_.end()) {
auto eol_notification =
eol_notification_handler_test_factory_.is_null()
? std::make_unique<EolNotification>(profile)
: eol_notification_handler_test_factory_.Run(profile);
iter = eol_notification_handler_
.insert(std::make_pair(profile, std::move(eol_notification)))
.first;
}
iter->second->CheckEolInfo();
}
void UserSessionManager::DoBrowserLaunchInternal(Profile* profile,
bool locale_pref_checked) {
TRACE_EVENT0("login", "UserSessionManager::DoBrowserLaunchInternal");
if (browser_shutdown::IsTryingToQuit() ||
chrome::IsSendingStopRequestToSessionManager())
return;
if (!locale_pref_checked) {
RespectLocalePreferenceWrapper(
profile,
base::BindRepeating(&UserSessionManager::DoBrowserLaunchInternal,
GetUserSessionManagerAsWeakPtr(), profile,
/*locale_pref_checked=*/true));
return;
}
// Call this before `RestartToApplyPerSessionFlagsIfNeed()` in the login
// process.
BrowserDataMigratorImpl::ClearMigrationStep(g_browser_process->local_state());
if (RestartToApplyPerSessionFlagsIfNeed(profile, false))
return;
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(profile);
if (BrowserDataMigratorImpl::MaybeRestartToMigrate(
user->GetAccountId(), user->username_hash(),
crosapi::browser_util::PolicyInitState::kAfterInit)) {
LOG(WARNING) << "Restarting chrome to run profile migration.";
return;
}
if (BrowserDataBackMigrator::MaybeRestartToMigrateBack(
user->GetAccountId(), user->username_hash(),
crosapi::browser_util::PolicyInitState::kAfterInit)) {
LOG(WARNING) << "Restarting chrome to run backward profile migration.";
return;
}
if (LoginDisplayHost::default_host()) {
LoginDisplayHost::default_host()->SetStatusAreaVisible(true);
LoginDisplayHost::default_host()->BeforeSessionStart();
}
BootTimesRecorder::Get()->AddLoginTimeMarker("BrowserLaunched", false);
VLOG(1) << "Launching browser...";
TRACE_EVENT0("login", "LaunchBrowser");
if (should_launch_browser_) {
if (floating_workspace_util::IsFloatingWorkspaceV1Enabled() ||
floating_workspace_util::IsFloatingWorkspaceV2Enabled()) {
// If floating workspace is enabled, it will override full restore.
FloatingWorkspaceService* floating_workspace_service =
FloatingWorkspaceService::GetForProfile(profile);
if (floating_workspace_util::IsFloatingWorkspaceV1Enabled() &&
floating_workspace_service) {
floating_workspace_service->SubscribeToForeignSessionUpdates();
}
} else if (!IsFullRestoreEnabled(profile)) {
LaunchBrowser(profile);
PerformPostBrowserLaunchOOBEActions(profile);
} else {
full_restore::FullRestoreService::GetForProfile(profile)
->LaunchBrowserWhenReady();
}
}
if (ProfileHelper::IsPrimaryProfile(profile)) {
// chrome::SessionRestore is using synchronous mode on Chrome OS,
// which means that data is definitely loaded by this moment.
Shell::Get()
->login_unlock_throughput_recorder()
->BrowserSessionRestoreDataLoaded();
}
if (HatsNotificationController::ShouldShowSurveyToProfile(
profile, kHatsGeneralSurvey)) {
hats_notification_controller_ =
new HatsNotificationController(profile, kHatsGeneralSurvey);
} else if (HatsNotificationController::ShouldShowSurveyToProfile(
profile, kHatsEntSurvey)) {
hats_notification_controller_ =
new HatsNotificationController(profile, kHatsEntSurvey);
} else if (HatsNotificationController::ShouldShowSurveyToProfile(
profile, kHatsStabilitySurvey)) {
hats_notification_controller_ =
new HatsNotificationController(profile, kHatsStabilitySurvey);
} else if (HatsNotificationController::ShouldShowSurveyToProfile(
profile, kHatsPerformanceSurvey)) {
hats_notification_controller_ =
new HatsNotificationController(profile, kHatsPerformanceSurvey);
} else if (HatsNotificationController::ShouldShowSurveyToProfile(
profile, kHatsBatteryLifeSurvey)) {
hats_notification_controller_ =
new HatsNotificationController(profile, kHatsBatteryLifeSurvey);
} else if (HatsNotificationController::ShouldShowSurveyToProfile(
profile, kHatsPeripheralsSurvey)) {
hats_notification_controller_ =
new HatsNotificationController(profile, kHatsPeripheralsSurvey);
}
base::OnceClosure login_host_finalized_callback = base::BindOnce(
[](uint64_t trace_id) {
session_manager::SessionManager::Get()->SessionStarted();
TRACE_EVENT_NESTABLE_ASYNC_END0(kEventCategoryChromeOS,
kEventStartSession,
TRACE_ID_LOCAL(trace_id));
},
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)));
// Mark login host for deletion after browser starts. This
// guarantees that the message loop will be referenced by the
// browser before it is dereferenced by the login host.
// TODO(crbug.com/1267769): `login_host` Finalize called twice, but it
// shouldn't. Remove DumpWithoutCrashing when we know the root cause.
if (LoginDisplayHost::default_host()) {
if (!LoginDisplayHost::default_host()->IsFinalizing()) {
LoginDisplayHost::default_host()->Finalize(
std::move(login_host_finalized_callback));
} else {
base::debug::DumpWithoutCrashing();
}
} else {
std::move(login_host_finalized_callback).Run();
}
BootTimesRecorder::Get()->LoginDone(
user_manager::UserManager::Get()->IsCurrentUserNew());
// Check to see if this profile should show EndOfLife Notification and show
// the message accordingly.
CheckEolInfo(profile);
ShowNotificationsIfNeeded(profile);
}
void UserSessionManager::RespectLocalePreferenceWrapper(
Profile* profile,
base::OnceClosure callback) {
if (browser_shutdown::IsTryingToQuit() ||
chrome::IsSendingStopRequestToSessionManager())
return;
const user_manager::User* const user =
ProfileHelper::Get()->GetUserByProfile(profile);
// RespectLocalePreference() will only invoke the callback on success. Split
// it here so we can invoke the callback separately on failure.
auto split_callback = base::SplitOnceCallback(std::move(callback));
locale_util::SwitchLanguageCallback locale_switched_callback(base::BindOnce(
&UserSessionManager::RunCallbackOnLocaleLoaded,
std::move(split_callback.first),
base::Owned(new InputEventsBlocker))); // Block UI events until
// the ResourceBundle is
// reloaded.
if (!RespectLocalePreference(profile, user,
std::move(locale_switched_callback))) {
std::move(split_callback.second).Run();
}
}
void UserSessionManager::LaunchBrowser(Profile* profile) {
StartupBrowserCreator browser_creator;
chrome::startup::IsFirstRun first_run =
::first_run::IsChromeFirstRun() ? chrome::startup::IsFirstRun::kYes
: chrome::startup::IsFirstRun::kNo;
browser_creator.LaunchBrowser(
*base::CommandLine::ForCurrentProcess(), profile, base::FilePath(),
chrome::startup::IsProcessStartup::kYes, first_run,
std::make_unique<OldLaunchModeRecorder>());
}
// static
void UserSessionManager::RunCallbackOnLocaleLoaded(
base::OnceClosure callback,
InputEventsBlocker* /* input_events_blocker */,
const locale_util::LanguageSwitchResult& /* result */) {
std::move(callback).Run();
}
void UserSessionManager::RemoveProfileForTesting(Profile* profile) {
default_ime_states_.erase(profile);
}
void UserSessionManager::InjectAuthenticatorBuilder(
std::unique_ptr<AuthenticatorBuilder> builder) {
injected_authenticator_builder_ = std::move(builder);
authenticator_.reset();
}
void UserSessionManager::OnTokenHandleObtained(const AccountId& account_id,
bool success) {
if (!success)
LOG(ERROR) << "OAuth2 token handle fetch failed.";
token_handle_fetcher_.reset();
}
bool UserSessionManager::TokenHandlesEnabled() {
if (!should_obtain_handles_)
return false;
bool show_names_on_signin = true;
auto* cros_settings = CrosSettings::Get();
cros_settings->GetBoolean(kAccountsPrefShowUserNamesOnSignIn,
&show_names_on_signin);
return show_names_on_signin;
}
void UserSessionManager::Shutdown() {
token_handle_fetcher_.reset();
token_handle_util_.reset();
token_observers_.clear();
always_on_vpn_manager_.reset();
u2f_notification_.reset();
help_app_notification_controller_.reset();
password_service_voted_.reset();
password_was_saved_ = false;
secure_dns_manager_.reset();
xdr_manager_.reset();
}
void UserSessionManager::SetSwitchesForUser(
const AccountId& account_id,
CommandLineSwitchesType switches_type,
const std::vector<std::string>& switches) {
// TODO(pmarko): Introduce a CHECK that `account_id` is the primary user
// (https://crbug.com/832857).
// Early out so that switches for secondary users are not applied to the whole
// session. This could be removed when things like flags UI of secondary users
// are fixed properly and TODO above to add CHECK() is done.
if (user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId() !=
account_id) {
return;
}
command_line_switches_[switches_type] = switches;
// Apply all command-line switch types in session manager as a flat list.
std::vector<std::string> all_switches;
for (const auto& pair : command_line_switches_) {
all_switches.insert(all_switches.end(), pair.second.begin(),
pair.second.end());
}
SessionManagerClient::Get()->SetFlagsForUser(
cryptohome::CreateAccountIdentifierFromAccountId(account_id),
all_switches);
}
void UserSessionManager::MaybeShowU2FNotification() {
if (!u2f_notification_) {
u2f_notification_ = std::make_unique<U2FNotification>();
u2f_notification_->Check();
}
}
void UserSessionManager::MaybeShowUpdateNotification(Profile* profile) {
if (!ProfileHelper::IsPrimaryProfile(profile)) {
return;
}
if (features::IsUpdateNotificationEnabled() && !update_notification_) {
GetUpdateNotificationShowingController(profile)
->MaybeShowUpdateNotification();
}
}
void UserSessionManager::MaybeShowHelpAppReleaseNotesNotification(
Profile* profile) {
if (!ProfileHelper::IsPrimaryProfile(profile))
return;
GetHelpAppNotificationController(profile)
->MaybeShowReleaseNotesNotification();
}
void UserSessionManager::SetEolNotificationHandlerFactoryForTesting(
const EolNotificationHandlerFactoryCallback&
eol_notification_handler_factory) {
eol_notification_handler_test_factory_ = eol_notification_handler_factory;
}
base::WeakPtr<UserSessionManager>
UserSessionManager::GetUserSessionManagerAsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void UserSessionManager::MaybeShowHelpAppDiscoverNotification(
Profile* profile) {
if (!ProfileHelper::IsPrimaryProfile(profile))
return;
GetHelpAppNotificationController(profile)->MaybeShowDiscoverNotification();
}
void UserSessionManager::CreateTokenUtilIfMissing() {
if (!token_handle_util_.get())
token_handle_util_ = std::make_unique<TokenHandleUtil>();
}
void UserSessionManager::UpdateTokenHandleIfRequired(
Profile* const profile,
const AccountId& account_id) {
if (!token_handle_util_->ShouldObtainHandle(account_id))
return;
if (token_handle_fetcher_.get())
return;
UpdateTokenHandle(profile, account_id);
}
void UserSessionManager::UpdateTokenHandle(Profile* const profile,
const AccountId& account_id) {
token_handle_fetcher_ = std::make_unique<TokenHandleFetcher>(
profile, token_handle_util_.get(), account_id);
token_handle_fetcher_->BackfillToken(
base::BindOnce(&UserSessionManager::OnTokenHandleObtained,
GetUserSessionManagerAsWeakPtr()));
token_handle_backfill_tried_for_testing_ = true;
}
bool UserSessionManager::IsFullRestoreEnabled(Profile* profile) {
auto* full_restore_service =
full_restore::FullRestoreService::GetForProfile(profile);
return full_restore_service != nullptr;
}
void UserSessionManager::OnUserEligibleForOnboardingSurvey(Profile* profile) {
onboarding_user_activity_counter_.reset();
if (profile != ProfileManager::GetActiveUserProfile())
return;
DCHECK(!session_manager::SessionManager::Get()->IsUserSessionBlocked());
// Do not run more than one HATS survey.
if (hats_notification_controller_)
return;
if (!HatsNotificationController::ShouldShowSurveyToProfile(
profile, kHatsOnboardingSurvey)) {
return;
}
hats_notification_controller_ =
new HatsNotificationController(profile, kHatsOnboardingSurvey);
}
void UserSessionManager::LoadShillProfile(const AccountId& account_id) {
SessionManagerClient::Get()->LoadShillProfile(
cryptohome::CreateAccountIdentifierFromAccountId(account_id));
}
HelpAppNotificationController*
UserSessionManager::GetHelpAppNotificationController(Profile* profile) {
if (!help_app_notification_controller_) {
help_app_notification_controller_ =
std::make_unique<HelpAppNotificationController>(profile);
}
return help_app_notification_controller_.get();
}
UpdateNotificationShowingController*
UserSessionManager::GetUpdateNotificationShowingController(Profile* profile) {
if (!update_notification_showing_controller_) {
update_notification_showing_controller_ =
std::make_unique<UpdateNotificationShowingController>(profile);
}
return update_notification_showing_controller_.get();
}
} // namespace ash