blob: 6ec4ab0104f4d4e3ec4cb8d5e32a152863840e41 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/ash/login_screen_client_impl.h"
#include <utility>
#include "ash/public/cpp/child_accounts/parent_access_controller.h"
#include "ash/public/cpp/login_screen.h"
#include "ash/public/cpp/login_screen_model.h"
#include "base/check_is_test.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.h"
#include "chrome/browser/ash/login/existing_user_controller.h"
#include "chrome/browser/ash/login/hats_unlock_survey_trigger.h"
#include "chrome/browser/ash/login/help_app_launcher.h"
#include "chrome/browser/ash/login/lock/screen_locker.h"
#include "chrome/browser/ash/login/login_auth_recorder.h"
#include "chrome/browser/ash/login/login_pref_names.h"
#include "chrome/browser/ash/login/reauth_stats.h"
#include "chrome/browser/ash/login/screens/user_selection_screen.h"
#include "chrome/browser/ash/login/startup_utils.h"
#include "chrome/browser/ash/login/ui/login_display_host.h"
#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
#include "chrome/browser/ash/login/ui/user_adding_screen.h"
#include "chrome/browser/ash/settings/cros_settings.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
#include "chrome/browser/ui/settings_window_manager_chromeos.h"
#include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_reauth_dialogs.h"
#include "chrome/browser/ui/webui/ash/login/l10n_util.h"
#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom-shared.h"
#include "chrome/common/webui_url_constants.h"
#include "chromeos/ash/components/settings/cros_settings_provider.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/user_names.h"
#include "ui/base/ime/ash/input_method_manager.h"
namespace {
using ash::SupervisedAction;
LoginScreenClientImpl* g_login_screen_client_instance = nullptr;
} // namespace
LoginScreenClientImpl::Delegate::Delegate() = default;
LoginScreenClientImpl::Delegate::~Delegate() = default;
LoginScreenClientImpl::ParentAccessDelegate::~ParentAccessDelegate() = default;
LoginScreenClientImpl::LoginScreenClientImpl()
: auth_recorder_(std::make_unique<ash::LoginAuthRecorder>()),
unlock_survey_trigger_(std::make_unique<ash::HatsUnlockSurveyTrigger>()) {
// Register this object as the client interface implementation.
ash::LoginScreen::Get()->SetClient(this);
DCHECK(!g_login_screen_client_instance);
g_login_screen_client_instance = this;
if (user_manager::UserManager::IsInitialized()) {
user_manager::UserManager::Get()->AddObserver(this);
} else {
CHECK_IS_TEST();
}
}
LoginScreenClientImpl::~LoginScreenClientImpl() {
if (user_manager::UserManager::IsInitialized()) {
user_manager::UserManager::Get()->RemoveObserver(this);
} else {
CHECK_IS_TEST();
}
ash::LoginScreen::Get()->SetClient(nullptr);
DCHECK_EQ(this, g_login_screen_client_instance);
g_login_screen_client_instance = nullptr;
}
// static
bool LoginScreenClientImpl::HasInstance() {
return !!g_login_screen_client_instance;
}
// static
LoginScreenClientImpl* LoginScreenClientImpl::Get() {
DCHECK(g_login_screen_client_instance);
return g_login_screen_client_instance;
}
void LoginScreenClientImpl::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
}
void LoginScreenClientImpl::AddSystemTrayObserver(
ash::SystemTrayObserver* observer) {
system_tray_observers_.AddObserver(observer);
}
void LoginScreenClientImpl::RemoveSystemTrayObserver(
ash::SystemTrayObserver* observer) {
system_tray_observers_.RemoveObserver(observer);
}
void LoginScreenClientImpl::AddLoginScreenShownObserver(
LoginScreenShownObserver* observer) {
login_screen_shown_observers_.AddObserver(observer);
}
void LoginScreenClientImpl::RemoveLoginScreenShownObserver(
LoginScreenShownObserver* observer) {
login_screen_shown_observers_.RemoveObserver(observer);
}
ash::LoginAuthRecorder* LoginScreenClientImpl::auth_recorder() {
return auth_recorder_.get();
}
void LoginScreenClientImpl::AuthenticateUserWithPasswordOrPin(
const AccountId& account_id,
const std::string& password,
bool authenticated_by_pin,
base::OnceCallback<void(bool)> callback) {
if (delegate_) {
delegate_->HandleAuthenticateUserWithPasswordOrPin(
account_id, password, authenticated_by_pin, std::move(callback));
auto auth_method = authenticated_by_pin
? ash::LoginAuthRecorder::AuthMethod::kPin
: ash::LoginAuthRecorder::AuthMethod::kPassword;
auth_recorder_->RecordAuthMethod(auth_method);
unlock_survey_trigger_->ShowSurveyIfSelected(account_id, auth_method);
} else {
LOG(ERROR) << "Failed AuthenticateUserWithPasswordOrPin; no delegate";
std::move(callback).Run(false);
}
}
void LoginScreenClientImpl::AuthenticateUserWithEasyUnlock(
const AccountId& account_id) {
if (delegate_) {
delegate_->HandleAuthenticateUserWithEasyUnlock(account_id);
auth_recorder_->RecordAuthMethod(
ash::LoginAuthRecorder::AuthMethod::kSmartlock);
unlock_survey_trigger_->ShowSurveyIfSelected(
account_id, ash::LoginAuthRecorder::AuthMethod::kSmartlock);
}
}
void LoginScreenClientImpl::AuthenticateUserWithChallengeResponse(
const AccountId& account_id,
base::OnceCallback<void(bool)> callback) {
if (delegate_) {
delegate_->HandleAuthenticateUserWithChallengeResponse(account_id,
std::move(callback));
auth_recorder_->RecordAuthMethod(
ash::LoginAuthRecorder::AuthMethod::kChallengeResponse);
unlock_survey_trigger_->ShowSurveyIfSelected(
account_id, ash::LoginAuthRecorder::AuthMethod::kChallengeResponse);
}
}
ash::ParentCodeValidationResult LoginScreenClientImpl::ValidateParentAccessCode(
const AccountId& account_id,
const std::string& access_code,
base::Time validation_time) {
return ash::parent_access::ParentAccessService::Get()
.ValidateParentAccessCode(account_id, access_code, validation_time);
}
void LoginScreenClientImpl::OnFocusPod(const AccountId& account_id) {
if (delegate_)
delegate_->HandleOnFocusPod(account_id);
}
void LoginScreenClientImpl::OnNoPodFocused() {
if (delegate_)
delegate_->HandleOnNoPodFocused();
}
void LoginScreenClientImpl::FocusLockScreenApps(bool reverse) {
// If delegate is not set, or it fails to handle focus request, call
// |HandleFocusLeavingLockScreenApps| so the lock screen service can
// give focus to the next window in the tab order.
if (!delegate_ || !delegate_->HandleFocusLockScreenApps(reverse)) {
ash::LoginScreen::Get()->GetModel()->HandleFocusLeavingLockScreenApps(
reverse);
}
}
void LoginScreenClientImpl::FocusOobeDialog() {
if (delegate_)
delegate_->HandleFocusOobeDialog();
}
void LoginScreenClientImpl::ShowGaiaSignin(const AccountId& prefilled_account) {
if (time_show_gaia_signin_initiated_.is_null())
time_show_gaia_signin_initiated_ = base::TimeTicks::Now();
// Check trusted status as a workaround to ensure that device owner id is
// ready. Device owner ID is necessary for IsApprovalRequired checks.
const ash::CrosSettingsProvider::TrustedStatus status =
ash::CrosSettings::Get()->PrepareTrustedValues(
base::BindOnce(&LoginScreenClientImpl::ShowGaiaSignin,
weak_ptr_factory_.GetWeakPtr(), prefilled_account));
switch (status) {
case ash::CrosSettingsProvider::TRUSTED:
// Owner account ID is available. Record time spent waiting for owner
// account ID and continue showing Gaia Signin.
base::UmaHistogramTimes(
"Ash.Login.ShowGaiaSignin.WaitTime",
base::TimeTicks::Now() - time_show_gaia_signin_initiated_);
time_show_gaia_signin_initiated_ = base::TimeTicks();
break;
case ash::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
// Do nothing. This function will be called again when the values are
// ready.
return;
case ash::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
base::UmaHistogramBoolean("Ash.Login.ShowGaiaSignin.PermanentlyUntrusted",
true);
time_show_gaia_signin_initiated_ = base::TimeTicks();
return;
}
auto supervised_action = prefilled_account.empty()
? SupervisedAction::kAddUser
: SupervisedAction::kReauth;
if (ash::parent_access::ParentAccessService::Get().IsApprovalRequired(
supervised_action)) {
// Show the client native parent access widget and processed to GAIA signin
// flow in |OnParentAccessValidation| when validation success.
// On login screen we want to validate parent access code for the
// device owner. Device owner might be different than the account that
// requires reauth, so we are passing an empty |account_id|.
ash::ParentAccessController::Get()->ShowWidget(
AccountId(),
base::BindOnce(&LoginScreenClientImpl::OnParentAccessValidation,
weak_ptr_factory_.GetWeakPtr(), prefilled_account),
supervised_action, false /* extra_dimmer */, base::Time::Now());
} else {
ShowGaiaSigninInternal(prefilled_account);
}
}
void LoginScreenClientImpl::ShowOsInstallScreen() {
if (ash::LoginDisplayHost::default_host()) {
ash::LoginDisplayHost::default_host()->ShowOsInstallScreen();
}
}
void LoginScreenClientImpl::OnRemoveUserWarningShown() {
ProfileMetrics::LogProfileDeleteUser(
ProfileMetrics::DELETE_PROFILE_USER_MANAGER_SHOW_WARNING);
}
void LoginScreenClientImpl::RemoveUser(const AccountId& account_id) {
ProfileMetrics::LogProfileDeleteUser(
ProfileMetrics::DELETE_PROFILE_USER_MANAGER);
user_manager::UserManager::Get()->RemoveUser(
account_id, user_manager::UserRemovalReason::LOCAL_USER_INITIATED);
if (ash::LoginDisplayHost::default_host())
ash::LoginDisplayHost::default_host()->UpdateAddUserButtonStatus();
}
void LoginScreenClientImpl::LaunchPublicSession(
const AccountId& account_id,
const std::string& locale,
const std::string& input_method) {
if (delegate_)
delegate_->HandleLaunchPublicSession(account_id, locale, input_method);
}
void LoginScreenClientImpl::RequestPublicSessionKeyboardLayouts(
const AccountId& account_id,
const std::string& locale) {
ash::GetKeyboardLayoutsForLocale(
base::BindOnce(&LoginScreenClientImpl::SetPublicSessionKeyboardLayout,
weak_ptr_factory_.GetWeakPtr(), account_id, locale),
locale, ash::input_method::InputMethodManager::Get());
}
void LoginScreenClientImpl::HandleAccelerator(
ash::LoginAcceleratorAction action) {
if (ash::LoginDisplayHost::default_host())
ash::LoginDisplayHost::default_host()->HandleAccelerator(action);
}
void LoginScreenClientImpl::ShowAccountAccessHelpApp(
gfx::NativeWindow parent_window) {
base::MakeRefCounted<ash::HelpAppLauncher>(parent_window)
->ShowHelpTopic(ash::HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
}
void LoginScreenClientImpl::ShowParentAccessHelpApp() {
// Don't pass in a parent window so that the size of the help dialog is not
// bounded by its parent window.
base::MakeRefCounted<ash::HelpAppLauncher>(/*parent_window=*/nullptr)
->ShowHelpTopic(ash::HelpAppLauncher::HELP_PARENT_ACCESS_CODE);
}
void LoginScreenClientImpl::ShowLockScreenNotificationSettings() {
chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
ProfileManager::GetActiveUserProfile(),
std::string(chromeos::settings::mojom::kSecurityAndSignInSubpagePathV2) +
"?settingId=" +
base::NumberToString(static_cast<int>(
chromeos::settings::mojom::Setting::kLockScreenNotification)));
}
void LoginScreenClientImpl::OnFocusLeavingSystemTray(bool reverse) {
for (ash::SystemTrayObserver& observer : system_tray_observers_)
observer.OnFocusLeavingSystemTray(reverse);
}
void LoginScreenClientImpl::OnSystemTrayBubbleShown() {
for (ash::SystemTrayObserver& observer : system_tray_observers_)
observer.OnSystemTrayBubbleShown();
}
void LoginScreenClientImpl::OnLoginScreenShown() {
for (LoginScreenShownObserver& observer : login_screen_shown_observers_)
observer.OnLoginScreenShown();
}
void LoginScreenClientImpl::LoadWallpaper(const AccountId& account_id) {
WallpaperControllerClientImpl::Get()->ShowUserWallpaper(account_id);
}
void LoginScreenClientImpl::SignOutUser() {
ash::ScreenLocker::default_screen_locker()->Signout();
}
void LoginScreenClientImpl::CancelAddUser() {
ash::UserAddingScreen::Get()->Cancel();
}
void LoginScreenClientImpl::LoginAsGuest() {
DCHECK(!ash::ScreenLocker::default_screen_locker());
if (ash::LoginDisplayHost::default_host()) {
ash::LoginDisplayHost::default_host()->GetExistingUserController()->Login(
ash::UserContext(user_manager::USER_TYPE_GUEST,
user_manager::GuestAccountId()),
ash::SigninSpecifics());
}
}
void LoginScreenClientImpl::ShowGuestTosScreen() {
// Guet ToS screen is only shown if EULA was not already accepted.
if (ash::StartupUtils::IsEulaAccepted()) {
LoginAsGuest();
return;
}
DCHECK(!ash::ScreenLocker::default_screen_locker());
if (ash::LoginDisplayHost::default_host()) {
ash::LoginDisplayHost::default_host()->ShowGuestTosScreen();
}
}
void LoginScreenClientImpl::OnMaxIncorrectPasswordAttempted(
const AccountId& account_id) {
RecordReauthReason(account_id, ash::ReauthReason::kIncorrectPasswordEntered);
}
void LoginScreenClientImpl::SetPublicSessionKeyboardLayout(
const AccountId& account_id,
const std::string& locale,
base::Value::List keyboard_layouts) {
std::vector<ash::InputMethodItem> result;
for (const auto& i : keyboard_layouts) {
if (!i.is_dict())
continue;
const base::Value::Dict& dict = i.GetDict();
ash::InputMethodItem input_method_item;
const std::string* ime_id = dict.FindString("value");
if (ime_id)
input_method_item.ime_id = *ime_id;
const std::string* title = dict.FindString("title");
if (title)
input_method_item.title = *title;
input_method_item.selected = dict.FindBool("selected").value_or(false);
result.push_back(std::move(input_method_item));
}
ash::LoginScreen::Get()->GetModel()->SetPublicSessionKeyboardLayouts(
account_id, locale, result);
}
views::Widget* LoginScreenClientImpl::GetLoginWindowWidget() {
if (ash::LoginDisplayHost::default_host()) {
return ash::LoginDisplayHost::default_host()->GetLoginWindowWidget();
}
return nullptr;
}
void LoginScreenClientImpl::OnUserImageChanged(const user_manager::User& user) {
ash::LoginScreen::Get()->GetModel()->SetAvatarForUser(
user.GetAccountId(),
ash::UserSelectionScreen::BuildAshUserAvatarForUser(user));
}
void LoginScreenClientImpl::OnParentAccessValidation(
const AccountId& prefilled_account,
bool success) {
if (success)
ShowGaiaSigninInternal(prefilled_account);
}
void LoginScreenClientImpl::ShowGaiaSigninInternal(
const AccountId& prefilled_account) {
if (ash::LoginDisplayHost::default_host()) {
// Login screen case.
ash::LoginDisplayHost::default_host()->ShowGaiaDialog(prefilled_account);
} else {
// Lock screen case.
ash::LockScreenStartReauthDialog::Show();
}
}