Chromium Code Reviews| Index: chrome/browser/chromeos/arc/arc_auth_service.cc |
| diff --git a/chrome/browser/chromeos/arc/arc_auth_service.cc b/chrome/browser/chromeos/arc/arc_auth_service.cc |
| index 7670436ec4d3efdee754692953cae00d1514077a..ca17a416f464f8f89920f344ed8ffdfd8bd0b4b6 100644 |
| --- a/chrome/browser/chromeos/arc/arc_auth_service.cc |
| +++ b/chrome/browser/chromeos/arc/arc_auth_service.cc |
| @@ -4,82 +4,28 @@ |
| #include "chrome/browser/chromeos/arc/arc_auth_service.h" |
| -#include <utility> |
| - |
| -#include "ash/common/shelf/shelf_delegate.h" |
| -#include "ash/common/wm_shell.h" |
| -#include "base/bind.h" |
| -#include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| -#include "base/strings/string16.h" |
| -#include "base/time/time.h" |
| +#include "base/memory/ptr_util.h" |
| #include "chrome/browser/chromeos/arc/arc_auth_code_fetcher.h" |
| -#include "chrome/browser/chromeos/arc/arc_auth_context.h" |
| #include "chrome/browser/chromeos/arc/arc_optin_uma.h" |
| -#include "chrome/browser/chromeos/arc/arc_support_host.h" |
| +#include "chrome/browser/chromeos/arc/arc_optin_uma.h" |
| +#include "chrome/browser/chromeos/arc/arc_session_manager.h" |
| #include "chrome/browser/chromeos/arc/auth/arc_robot_auth.h" |
| -#include "chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.h" |
| -#include "chrome/browser/chromeos/arc/policy/arc_android_management_checker.h" |
| #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h" |
| -#include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| -#include "chrome/browser/policy/profile_policy_connector.h" |
| -#include "chrome/browser/policy/profile_policy_connector_factory.h" |
| -#include "chrome/browser/prefs/pref_service_syncable_util.h" |
| -#include "chrome/browser/profiles/profile.h" |
| -#include "chrome/browser/ui/app_list/arc/arc_app_launcher.h" |
| -#include "chrome/browser/ui/app_list/arc/arc_app_utils.h" |
| -#include "chrome/browser/ui/ash/multi_user/multi_user_util.h" |
| -#include "chrome/browser/ui/browser_commands.h" |
| -#include "chrome/common/pref_names.h" |
| -#include "chrome/grit/generated_resources.h" |
| #include "chromeos/chromeos_switches.h" |
| -#include "chromeos/cryptohome/cryptohome_parameters.h" |
| -#include "chromeos/dbus/dbus_thread_manager.h" |
| -#include "chromeos/dbus/session_manager_client.h" |
| #include "components/arc/arc_bridge_service.h" |
| -#include "components/pref_registry/pref_registry_syncable.h" |
| -#include "components/prefs/pref_service.h" |
| -#include "components/sync_preferences/pref_service_syncable.h" |
| -#include "components/user_manager/user.h" |
| #include "content/public/browser/browser_thread.h" |
| -#include "extensions/browser/extension_prefs.h" |
| namespace arc { |
| - |
| namespace { |
| -constexpr size_t kMinVersionForOnAccountInfoReady = 5; |
| - |
| -// Weak pointer. This class is owned by ArcServiceManager. |
| ArcAuthService* g_arc_auth_service = nullptr; |
| -// Skip creating UI in unit tests |
| -bool g_disable_ui_for_testing = false; |
| - |
| -// Use specified ash::ShelfDelegate for unit tests. |
| -ash::ShelfDelegate* g_shelf_delegate_for_testing = nullptr; |
| - |
| -// The Android management check is disabled by default, it's used only for |
| -// testing. |
| -bool g_enable_check_android_management_for_testing = false; |
| - |
| -// Maximum amount of time we'll wait for ARC to finish booting up. Once this |
| -// timeout expires, keep ARC running in case the user wants to file feedback, |
| -// but present the UI to try again. |
| -constexpr base::TimeDelta kArcSignInTimeout = base::TimeDelta::FromMinutes(5); |
| - |
| -ash::ShelfDelegate* GetShelfDelegate() { |
| - if (g_shelf_delegate_for_testing) |
| - return g_shelf_delegate_for_testing; |
| - if (ash::WmShell::HasInstance()) { |
| - DCHECK(ash::WmShell::Get()->shelf_delegate()); |
| - return ash::WmShell::Get()->shelf_delegate(); |
| - } |
| - return nullptr; |
| -} |
| +constexpr uint32_t kMinVersionForOnAccountInfoReady = 5; |
| +// Convers mojom::ArcSignInFailureReason into ProvisiningResult. |
| ProvisioningResult ConvertArcSignInFailureReasonToProvisioningResult( |
| mojom::ArcSignInFailureReason reason) { |
| using ArcSignInFailureReason = mojom::ArcSignInFailureReason; |
| @@ -111,14 +57,10 @@ ProvisioningResult ConvertArcSignInFailureReasonToProvisioningResult( |
| return ProvisioningResult::UNKNOWN_ERROR; |
| } |
| -bool IsArcKioskMode() { |
| - return user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp(); |
| -} |
| - |
| mojom::ChromeAccountType GetAccountType() { |
| - if (IsArcKioskMode()) |
| - return mojom::ChromeAccountType::ROBOT_ACCOUNT; |
| - return mojom::ChromeAccountType::USER_ACCOUNT; |
| + return ArcSessionManager::IsArcKioskMode() |
| + ? mojom::ChromeAccountType::ROBOT_ACCOUNT |
| + : mojom::ChromeAccountType::USER_ACCOUNT; |
| } |
| } // namespace |
| @@ -180,163 +122,53 @@ class ArcAuthService::AccountInfoNotifier { |
| ArcAuthService::ArcAuthService(ArcBridgeService* bridge_service) |
| : ArcService(bridge_service), binding_(this), weak_ptr_factory_(this) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(!g_arc_auth_service); |
| - |
| g_arc_auth_service = this; |
| - |
| - arc_bridge_service()->AddObserver(this); |
| arc_bridge_service()->auth()->AddObserver(this); |
| } |
| ArcAuthService::~ArcAuthService() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK_EQ(this, g_arc_auth_service); |
| - |
| - Shutdown(); |
| arc_bridge_service()->auth()->RemoveObserver(this); |
| - arc_bridge_service()->RemoveObserver(this); |
| + DCHECK(g_arc_auth_service == this); |
|
xiyuan
2016/11/16 18:55:52
nit: DCHECK_EQ(this, g_arc_auth_service);
hidehiko
2016/11/16 19:52:05
Done.
|
| g_arc_auth_service = nullptr; |
| } |
| // static |
| -ArcAuthService* ArcAuthService::Get() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| +ArcAuthService* ArcAuthService::GetForTest() { |
| + DCHECK(g_arc_auth_service); |
| return g_arc_auth_service; |
| } |
| -// static |
| -void ArcAuthService::RegisterProfilePrefs( |
| - user_prefs::PrefRegistrySyncable* registry) { |
| - // TODO(dspaid): Implement a mechanism to allow this to sync on first boot |
| - // only. |
| - registry->RegisterBooleanPref(prefs::kArcEnabled, false); |
| - registry->RegisterBooleanPref(prefs::kArcSignedIn, false); |
| - registry->RegisterBooleanPref(prefs::kArcTermsAccepted, false); |
| - registry->RegisterBooleanPref(prefs::kArcBackupRestoreEnabled, true); |
| - registry->RegisterBooleanPref(prefs::kArcLocationServiceEnabled, true); |
| -} |
| - |
| -// static |
| -void ArcAuthService::DisableUIForTesting() { |
| - g_disable_ui_for_testing = true; |
| -} |
| - |
| -// static |
| -void ArcAuthService::SetShelfDelegateForTesting( |
| - ash::ShelfDelegate* shelf_delegate) { |
| - g_shelf_delegate_for_testing = shelf_delegate; |
| -} |
| - |
| -// static |
| -bool ArcAuthService::IsOptInVerificationDisabled() { |
| - return base::CommandLine::ForCurrentProcess()->HasSwitch( |
| - chromeos::switches::kDisableArcOptInVerification); |
| -} |
| - |
| -// static |
| -void ArcAuthService::EnableCheckAndroidManagementForTesting() { |
| - g_enable_check_android_management_for_testing = true; |
| -} |
| - |
| -// static |
| -bool ArcAuthService::IsAllowedForProfile(const Profile* profile) { |
| - if (!ArcBridgeService::GetEnabled(base::CommandLine::ForCurrentProcess())) { |
| - VLOG(1) << "Arc is not enabled."; |
| - return false; |
| - } |
| - |
| - if (!profile) { |
| - VLOG(1) << "ARC is not supported for systems without profile."; |
| - return false; |
| - } |
| - |
| - if (!chromeos::ProfileHelper::IsPrimaryProfile(profile)) { |
| - VLOG(1) << "Non-primary users are not supported in ARC."; |
| - return false; |
| - } |
| - |
| - if (profile->IsLegacySupervised()) { |
| - VLOG(1) << "Supervised users are not supported in ARC."; |
| - return false; |
| - } |
| - |
| - user_manager::User const* const user = |
| - chromeos::ProfileHelper::Get()->GetUserByProfile(profile); |
| - if ((!user || !user->HasGaiaAccount()) && !IsArcKioskMode()) { |
| - VLOG(1) << "Users without GAIA accounts are not supported in ARC."; |
| - return false; |
| - } |
| - |
| - if (user_manager::UserManager::Get() |
| - ->IsCurrentUserCryptohomeDataEphemeral()) { |
| - VLOG(2) << "Users with ephemeral data are not supported in Arc."; |
| - return false; |
| - } |
| - |
| - return true; |
| -} |
| - |
| void ArcAuthService::OnInstanceReady() { |
| auto* instance = arc_bridge_service()->auth()->GetInstanceForMethod("Init"); |
| DCHECK(instance); |
| instance->Init(binding_.CreateInterfacePtrAndBind()); |
| } |
| -void ArcAuthService::OnBridgeStopped(ArcBridgeService::StopReason reason) { |
| - // TODO(crbug.com/625923): Use |reason| to report more detailed errors. |
| - if (arc_sign_in_timer_.IsRunning()) { |
| - OnProvisioningFinished(ProvisioningResult::ARC_STOPPED); |
| - } |
| - |
| - if (clear_required_) { |
| - // This should be always true, but just in case as this is looked at |
| - // inside RemoveArcData() at first. |
| - DCHECK(arc_bridge_service()->stopped()); |
| - RemoveArcData(); |
| - } else { |
| - // To support special "Stop and enable ARC" procedure for enterprise, |
| - // here call OnArcDataRemoved(true) as if the data removal is successfully |
| - // done. |
| - // TODO(hidehiko): Restructure the code. crbug.com/665316 |
| - base::ThreadTaskRunnerHandle::Get()->PostTask( |
| - FROM_HERE, base::Bind(&ArcAuthService::OnArcDataRemoved, |
| - weak_ptr_factory_.GetWeakPtr(), true)); |
| - } |
| +void ArcAuthService::OnInstanceClosed() { |
| + ArcSupportHost* support_host = ArcSessionManager::Get()->support_host(); |
| + if (support_host) |
| + support_host->RemoveObserver(this); |
| + notifier_.reset(); |
| + arc_robot_auth_.reset(); |
| + auth_code_fetcher_.reset(); |
| } |
| -void ArcAuthService::RemoveArcData() { |
| - if (!arc_bridge_service()->stopped()) { |
| - // Just set a flag. On bridge stopped, this will be re-called, |
| - // then session manager should remove the data. |
| - clear_required_ = true; |
| - return; |
| - } |
| - clear_required_ = false; |
| - chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->RemoveArcData( |
| - cryptohome::Identification( |
| - multi_user_util::GetAccountIdFromProfile(profile_)), |
| - base::Bind(&ArcAuthService::OnArcDataRemoved, |
| - weak_ptr_factory_.GetWeakPtr())); |
| +void ArcAuthService::OnSignInComplete() { |
| + ArcSessionManager::Get()->OnProvisioningFinished(ProvisioningResult::SUCCESS); |
| } |
| -void ArcAuthService::OnArcDataRemoved(bool success) { |
| - LOG_IF(ERROR, !success) << "Required ARC user data wipe failed."; |
| - |
| - // Here check if |reenable_arc_| is marked or not. |
| - // The only case this happens should be in the special case for enterprise |
| - // "on managed lost" case. In that case, OnBridgeStopped() should trigger |
| - // the RemoveArcData(), then this. |
| - // TODO(hidehiko): Restructure the code. |
| - if (!reenable_arc_) |
| - return; |
| +void ArcAuthService::OnSignInFailed(mojom::ArcSignInFailureReason reason) { |
| + ArcSessionManager::Get()->OnProvisioningFinished( |
| + ConvertArcSignInFailureReasonToProvisioningResult(reason)); |
| +} |
| - // Restart ARC anyway. Let the enterprise reporting instance decide whether |
| - // the ARC user data wipe is still required or not. |
| - reenable_arc_ = false; |
| - VLOG(1) << "Reenable ARC"; |
| - EnableArc(); |
| +void ArcAuthService::RequestAccountInfo() { |
| + RequestAccountInfoInternal( |
| + base::MakeUnique<ArcAuthService::AccountInfoNotifier>( |
| + base::Bind(&ArcAuthService::OnAccountInfoReady, |
| + weak_ptr_factory_.GetWeakPtr()))); |
| } |
| void ArcAuthService::GetAuthCodeDeprecated0( |
| @@ -349,7 +181,7 @@ void ArcAuthService::GetAuthCodeDeprecated( |
| const GetAuthCodeDeprecatedCallback& callback) { |
| // For robot account we must use RequestAccountInfo because it allows |
| // to specify account type. |
| - DCHECK(!IsArcKioskMode()); |
| + DCHECK(!ArcSessionManager::IsArcKioskMode()); |
| RequestAccountInfoInternal( |
| base::MakeUnique<ArcAuthService::AccountInfoNotifier>(callback)); |
| } |
| @@ -360,40 +192,31 @@ void ArcAuthService::GetAuthCodeAndAccountTypeDeprecated( |
| base::MakeUnique<ArcAuthService::AccountInfoNotifier>(callback)); |
| } |
| -void ArcAuthService::RequestAccountInfo() { |
| - RequestAccountInfoInternal( |
| - base::MakeUnique<ArcAuthService::AccountInfoNotifier>( |
| - base::Bind(&ArcAuthService::OnAccountInfoReady, |
| - weak_ptr_factory_.GetWeakPtr()))); |
| -} |
| - |
| -void ArcAuthService::OnAccountInfoReady(mojom::AccountInfoPtr account_info) { |
| +void ArcAuthService::GetIsAccountManagedDeprecated( |
| + const GetIsAccountManagedDeprecatedCallback& callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - auto* instance = arc_bridge_service()->auth()->GetInstanceForMethod( |
| - "OnAccountInfoReady", kMinVersionForOnAccountInfoReady); |
| - DCHECK(instance); |
| - instance->OnAccountInfoReady(std::move(account_info)); |
| + callback.Run( |
| + policy_util::IsAccountManaged(ArcSessionManager::Get()->profile())); |
| } |
| void ArcAuthService::RequestAccountInfoInternal( |
| - std::unique_ptr<ArcAuthService::AccountInfoNotifier> |
| - account_info_notifier) { |
| + std::unique_ptr<AccountInfoNotifier> notifier) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| // No other auth code-related operation may be in progress. |
| - DCHECK(!account_info_notifier_); |
| + DCHECK(!notifier_); |
| - if (IsOptInVerificationDisabled()) { |
| - account_info_notifier->Notify(false /* = is_enforced */, std::string(), |
| - GetAccountType(), |
| - policy_util::IsAccountManaged(profile_)); |
| + if (ArcSessionManager::IsOptInVerificationDisabled()) { |
| + notifier->Notify( |
| + false /* = is_enforced */, std::string(), GetAccountType(), |
| + policy_util::IsAccountManaged(ArcSessionManager::Get()->profile())); |
| return; |
| } |
| // Hereafter asynchronous operation. Remember the notifier. |
| - account_info_notifier_ = std::move(account_info_notifier); |
| + notifier_ = std::move(notifier); |
| // In Kiosk mode, use Robot auth code fetching. |
| - if (IsArcKioskMode()) { |
| + if (ArcSessionManager::IsArcKioskMode()) { |
| arc_robot_auth_.reset(new ArcRobotAuth()); |
| arc_robot_auth_->FetchRobotAuthCode( |
| base::Bind(&ArcAuthService::OnRobotAuthCodeFetched, |
| @@ -405,28 +228,42 @@ void ArcAuthService::RequestAccountInfoInternal( |
| // fetching. |
| const base::CommandLine* command_line = |
| base::CommandLine::ForCurrentProcess(); |
| + std::string auth_endpoint; |
| if (command_line->HasSwitch(chromeos::switches::kArcUseAuthEndpoint)) { |
| - std::string auth_endpoint = command_line->GetSwitchValueASCII( |
| + auth_endpoint = command_line->GetSwitchValueASCII( |
| chromeos::switches::kArcUseAuthEndpoint); |
| - if (!auth_endpoint.empty()) { |
| - DCHECK(!auth_code_fetcher_); |
| - auth_code_fetcher_ = base::MakeUnique<ArcAuthCodeFetcher>( |
| - profile_, context_.get(), auth_endpoint); |
| - auth_code_fetcher_->Fetch(base::Bind(&ArcAuthService::OnAuthCodeFetched, |
| - weak_ptr_factory_.GetWeakPtr())); |
| - return; |
| - } |
| + } |
| + if (!auth_endpoint.empty()) { |
| + DCHECK(!auth_code_fetcher_); |
| + auth_code_fetcher_ = base::MakeUnique<ArcAuthCodeFetcher>( |
| + ArcSessionManager::Get()->profile(), |
| + ArcSessionManager::Get()->auth_context(), auth_endpoint); |
| + auth_code_fetcher_->Fetch(base::Bind(&ArcAuthService::OnAuthCodeFetched, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + return; |
| } |
| // Otherwise, show LSO page to user, and let them click "Sign in" button. |
| - if (support_host_) |
| - support_host_->ShowLso(); |
| + ArcSupportHost* support_host = ArcSessionManager::Get()->support_host(); |
| + if (support_host) { |
|
Luis Héctor Chávez
2016/11/16 17:13:34
What happens if this is not available? Wouldn't AR
hidehiko
2016/11/16 19:52:05
Oh, good point. I re-check this and it turned out
|
| + if (!support_host->HasObserver(this)) |
| + support_host->AddObserver(this); |
| + support_host->ShowLso(); |
| + } |
| +} |
| + |
| +void ArcAuthService::OnAccountInfoReady(mojom::AccountInfoPtr account_info) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + auto* instance = arc_bridge_service()->auth()->GetInstanceForMethod( |
| + "OnAccountInfoReady", kMinVersionForOnAccountInfoReady); |
| + DCHECK(instance); |
| + instance->OnAccountInfoReady(std::move(account_info)); |
| } |
| void ArcAuthService::OnRobotAuthCodeFetched( |
| const std::string& robot_auth_code) { |
| // We fetching robot auth code for ARC kiosk only. |
| - DCHECK(IsArcKioskMode()); |
| + DCHECK(ArcSessionManager::IsArcKioskMode()); |
| // Current instance of ArcRobotAuth became useless. |
| arc_robot_auth_.reset(); |
| @@ -447,7 +284,7 @@ void ArcAuthService::OnAuthCodeFetched(const std::string& auth_code) { |
| auth_code_fetcher_.reset(); |
| if (auth_code.empty()) { |
| - OnProvisioningFinished( |
| + ArcSessionManager::Get()->OnProvisioningFinished( |
| ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR); |
| return; |
| } |
| @@ -455,625 +292,29 @@ void ArcAuthService::OnAuthCodeFetched(const std::string& auth_code) { |
| OnAuthCodeObtained(auth_code); |
| } |
| -void ArcAuthService::OnSignInComplete() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK_EQ(state_, State::ACTIVE); |
| - OnProvisioningFinished(ProvisioningResult::SUCCESS); |
| -} |
| - |
| -void ArcAuthService::OnSignInFailed(mojom::ArcSignInFailureReason reason) { |
| - OnProvisioningFinished( |
| - ConvertArcSignInFailureReasonToProvisioningResult(reason)); |
| -} |
| - |
| -void ArcAuthService::OnProvisioningFinished(ProvisioningResult result) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK_EQ(state_, State::ACTIVE); |
| - |
| - if (result == ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR) { |
| - // For backword compatibility, use NETWORK_ERROR for |
| - // CHROME_SERVER_COMMUNICATION_ERROR case. |
| - UpdateOptInCancelUMA(OptInCancelReason::NETWORK_ERROR); |
| - } else if (!sign_in_time_.is_null()) { |
| - arc_sign_in_timer_.Stop(); |
| - |
| - UpdateProvisioningTiming(base::Time::Now() - sign_in_time_, |
| - result == ProvisioningResult::SUCCESS, |
| - policy_util::IsAccountManaged(profile_)); |
| - UpdateProvisioningResultUMA(result, |
| - policy_util::IsAccountManaged(profile_)); |
| - if (result != ProvisioningResult::SUCCESS) { |
| - UpdateOptInCancelUMA(OptInCancelReason::CLOUD_PROVISION_FLOW_FAIL); |
| - } |
| - } |
| - |
| - if (result == ProvisioningResult::SUCCESS) { |
| - if (support_host_) |
| - support_host_->Close(); |
| - |
| - if (profile_->GetPrefs()->GetBoolean(prefs::kArcSignedIn)) |
| - return; |
| - |
| - profile_->GetPrefs()->SetBoolean(prefs::kArcSignedIn, true); |
| - if (!IsOptInVerificationDisabled()) { |
| - playstore_launcher_.reset( |
| - new ArcAppLauncher(profile_, kPlayStoreAppId, true)); |
| - } |
| - |
| - for (auto& observer : observer_list_) |
| - observer.OnInitialStart(); |
| - return; |
| - } |
| - |
| - ArcSupportHost::Error error; |
| - switch (result) { |
| - case ProvisioningResult::GMS_NETWORK_ERROR: |
| - error = ArcSupportHost::Error::SIGN_IN_NETWORK_ERROR; |
| - break; |
| - case ProvisioningResult::GMS_SERVICE_UNAVAILABLE: |
| - case ProvisioningResult::GMS_SIGN_IN_FAILED: |
| - case ProvisioningResult::GMS_SIGN_IN_TIMEOUT: |
| - case ProvisioningResult::GMS_SIGN_IN_INTERNAL_ERROR: |
| - error = ArcSupportHost::Error::SIGN_IN_SERVICE_UNAVAILABLE_ERROR; |
| - break; |
| - case ProvisioningResult::GMS_BAD_AUTHENTICATION: |
| - error = ArcSupportHost::Error::SIGN_IN_BAD_AUTHENTICATION_ERROR; |
| - break; |
| - case ProvisioningResult::DEVICE_CHECK_IN_FAILED: |
| - case ProvisioningResult::DEVICE_CHECK_IN_TIMEOUT: |
| - case ProvisioningResult::DEVICE_CHECK_IN_INTERNAL_ERROR: |
| - error = ArcSupportHost::Error::SIGN_IN_GMS_NOT_AVAILABLE_ERROR; |
| - break; |
| - case ProvisioningResult::CLOUD_PROVISION_FLOW_FAILED: |
| - case ProvisioningResult::CLOUD_PROVISION_FLOW_TIMEOUT: |
| - case ProvisioningResult::CLOUD_PROVISION_FLOW_INTERNAL_ERROR: |
| - error = ArcSupportHost::Error::SIGN_IN_CLOUD_PROVISION_FLOW_FAIL_ERROR; |
| - break; |
| - case ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR: |
| - error = ArcSupportHost::Error::SERVER_COMMUNICATION_ERROR; |
| - break; |
| - default: |
| - error = ArcSupportHost::Error::SIGN_IN_UNKNOWN_ERROR; |
| - break; |
| - } |
| - |
| - if (result == ProvisioningResult::ARC_STOPPED || |
| - result == ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR) { |
| - if (profile_->GetPrefs()->HasPrefPath(prefs::kArcSignedIn)) |
| - profile_->GetPrefs()->SetBoolean(prefs::kArcSignedIn, false); |
| - ShutdownBridge(); |
| - if (support_host_) |
| - support_host_->ShowError(error, false); |
| - return; |
| - } |
| - |
| - if (result == ProvisioningResult::CLOUD_PROVISION_FLOW_FAILED || |
| - result == ProvisioningResult::CLOUD_PROVISION_FLOW_TIMEOUT || |
| - result == ProvisioningResult::CLOUD_PROVISION_FLOW_INTERNAL_ERROR || |
| - // OVERALL_SIGN_IN_TIMEOUT might be an indication that ARC believes it is |
| - // fully setup, but Chrome does not. |
| - result == ProvisioningResult::OVERALL_SIGN_IN_TIMEOUT || |
| - // Just to be safe, remove data if we don't know the cause. |
| - result == ProvisioningResult::UNKNOWN_ERROR) { |
| - RemoveArcData(); |
| - } |
| - |
| - // We'll delay shutting down the bridge in this case to allow people to send |
| - // feedback. |
| - if (support_host_) |
| - support_host_->ShowError(error, true /* = show send feedback button */); |
| -} |
| - |
| -void ArcAuthService::GetIsAccountManagedDeprecated( |
| - const GetIsAccountManagedDeprecatedCallback& callback) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - |
| - callback.Run(policy_util::IsAccountManaged(profile_)); |
| -} |
| - |
| -void ArcAuthService::SetState(State state) { |
| - state_ = state; |
| -} |
| - |
| -bool ArcAuthService::IsAllowed() const { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - return profile_ != nullptr; |
| -} |
| - |
| -void ArcAuthService::OnPrimaryUserProfilePrepared(Profile* profile) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK(profile && profile != profile_); |
| - |
| - Shutdown(); |
| - |
| - if (!IsAllowedForProfile(profile)) |
| - return; |
| - |
| - // TODO(khmel): Move this to IsAllowedForProfile. |
| - if (policy_util::IsArcDisabledForEnterprise() && |
| - policy_util::IsAccountManaged(profile)) { |
| - VLOG(2) << "Enterprise users are not supported in ARC."; |
| - return; |
| - } |
| - |
| - profile_ = profile; |
| - |
| - // Create the support host at initialization. Note that, practically, |
| - // ARC support Chrome app is rarely used (only opt-in and re-auth flow). |
| - // So, it may be better to initialize it lazily. |
| - // TODO(hidehiko): Revisit to think about lazy initialization. |
| - // |
| - // Don't show UI for ARC Kiosk because the only one UI in kiosk mode must |
| - // be the kiosk app. In case of error the UI will be useless as well, because |
| - // in typical use case there will be no one nearby the kiosk device, who can |
| - // do some action to solve the problem be means of UI. |
| - if (!g_disable_ui_for_testing && !IsOptInVerificationDisabled() && |
| - !IsArcKioskMode()) { |
| - DCHECK(!support_host_); |
| - support_host_ = base::MakeUnique<ArcSupportHost>(profile_); |
| - support_host_->AddObserver(this); |
| - |
| - preference_handler_ = base::MakeUnique<arc::ArcOptInPreferenceHandler>( |
| - this, profile_->GetPrefs()); |
| - // This automatically updates all preferences. |
| - preference_handler_->Start(); |
| - } |
| - |
| - DCHECK_EQ(State::NOT_INITIALIZED, state_); |
| - SetState(State::STOPPED); |
| - |
| - PrefServiceSyncableFromProfile(profile_)->AddSyncedPrefObserver( |
| - prefs::kArcEnabled, this); |
| - |
| - context_.reset(new ArcAuthContext(profile_)); |
| - |
| - // In case UI is disabled we assume that ARC is opted-in. For ARC Kiosk we |
| - // skip ToS because it is very likely that near the device there will be |
| - // no one who is eligible to accept them. We skip if Android management check |
| - // because there are no managed human users for Kiosk exist. |
| - if (IsOptInVerificationDisabled() || IsArcKioskMode()) { |
| - StartArc(); |
| - return; |
| - } |
| - |
| - if (!g_disable_ui_for_testing || |
| - g_enable_check_android_management_for_testing) { |
| - ArcAndroidManagementChecker::StartClient(); |
| - } |
| - pref_change_registrar_.Init(profile_->GetPrefs()); |
| - pref_change_registrar_.Add( |
| - prefs::kArcEnabled, base::Bind(&ArcAuthService::OnOptInPreferenceChanged, |
| - weak_ptr_factory_.GetWeakPtr())); |
| - if (profile_->GetPrefs()->GetBoolean(prefs::kArcEnabled)) { |
| - OnOptInPreferenceChanged(); |
| - } else { |
| - RemoveArcData(); |
| - PrefServiceSyncableFromProfile(profile_)->AddObserver(this); |
| - OnIsSyncingChanged(); |
| - } |
| -} |
| - |
| -void ArcAuthService::OnIsSyncingChanged() { |
| - sync_preferences::PrefServiceSyncable* const pref_service_syncable = |
| - PrefServiceSyncableFromProfile(profile_); |
| - if (!pref_service_syncable->IsSyncing()) |
| - return; |
| - |
| - pref_service_syncable->RemoveObserver(this); |
| - |
| - if (IsArcEnabled()) |
| - OnOptInPreferenceChanged(); |
| -} |
| - |
| -void ArcAuthService::Shutdown() { |
| - ShutdownBridge(); |
| - if (support_host_) { |
| - support_host_->Close(); |
| - support_host_->RemoveObserver(this); |
| - support_host_.reset(); |
| - } |
| - if (profile_) { |
| - sync_preferences::PrefServiceSyncable* pref_service_syncable = |
| - PrefServiceSyncableFromProfile(profile_); |
| - pref_service_syncable->RemoveObserver(this); |
| - pref_service_syncable->RemoveSyncedPrefObserver(prefs::kArcEnabled, this); |
| - } |
| - pref_change_registrar_.RemoveAll(); |
| - context_.reset(); |
| - profile_ = nullptr; |
| - arc_robot_auth_.reset(); |
| - SetState(State::NOT_INITIALIZED); |
| -} |
| - |
| -void ArcAuthService::OnSyncedPrefChanged(const std::string& path, |
| - bool from_sync) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - |
| - // Update UMA only for local changes |
| - if (!from_sync) { |
| - const bool arc_enabled = |
| - profile_->GetPrefs()->GetBoolean(prefs::kArcEnabled); |
| - UpdateOptInActionUMA(arc_enabled ? OptInActionType::OPTED_IN |
| - : OptInActionType::OPTED_OUT); |
| - |
| - if (!arc_enabled && !IsArcManaged()) { |
| - ash::ShelfDelegate* shelf_delegate = GetShelfDelegate(); |
| - if (shelf_delegate) |
| - shelf_delegate->UnpinAppWithID(ArcSupportHost::kHostAppId); |
| - } |
| - } |
| -} |
| - |
| -void ArcAuthService::StopArc() { |
| - if (state_ != State::STOPPED) { |
| - profile_->GetPrefs()->SetBoolean(prefs::kArcSignedIn, false); |
| - profile_->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, false); |
| - } |
| - ShutdownBridge(); |
| - if (support_host_) |
| - support_host_->Close(); |
| -} |
| - |
| -void ArcAuthService::OnOptInPreferenceChanged() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK(profile_); |
| - |
| - // TODO(dspaid): Move code from OnSyncedPrefChanged into this method. |
| - OnSyncedPrefChanged(prefs::kArcEnabled, IsArcManaged()); |
| - |
| - const bool arc_enabled = IsArcEnabled(); |
| - for (auto& observer : observer_list_) |
| - observer.OnOptInEnabled(arc_enabled); |
| - |
| - if (!arc_enabled) { |
| - StopArc(); |
| - RemoveArcData(); |
| - return; |
| - } |
| - |
| - if (state_ == State::ACTIVE) |
| - return; |
| - |
| - if (support_host_) |
| - support_host_->SetArcManaged(IsArcManaged()); |
| - |
| - if (!profile_->GetPrefs()->GetBoolean(prefs::kArcSignedIn)) { |
| - if (profile_->GetPrefs()->GetBoolean(prefs::kArcTermsAccepted)) { |
| - StartArc(); |
| - } else { |
| - // Need pre-fetch auth code and show OptIn UI if needed. |
| - StartUI(); |
| - } |
| - } else { |
| - // Ready to start Arc, but check Android management in parallel. |
| - StartArc(); |
| - // Note: Because the callback may be called in synchronous way (i.e. called |
| - // on the same stack), StartCheck() needs to be called *after* StartArc(). |
| - // Otherwise, DisableArc() which may be called in |
| - // OnBackgroundAndroidManagementChecked() could be ignored. |
| - if (!g_disable_ui_for_testing || |
| - g_enable_check_android_management_for_testing) { |
| - android_management_checker_.reset(new ArcAndroidManagementChecker( |
| - profile_, context_->token_service(), context_->account_id(), |
| - true /* retry_on_error */)); |
| - android_management_checker_->StartCheck( |
| - base::Bind(&ArcAuthService::OnBackgroundAndroidManagementChecked, |
| - weak_ptr_factory_.GetWeakPtr())); |
| - } |
| - } |
| -} |
| - |
| -void ArcAuthService::ShutdownBridge() { |
| - arc_sign_in_timer_.Stop(); |
| - playstore_launcher_.reset(); |
| - account_info_notifier_.reset(); |
| - android_management_checker_.reset(); |
| - auth_code_fetcher_.reset(); |
| - arc_bridge_service()->RequestStop(); |
| - if (state_ != State::NOT_INITIALIZED) |
| - SetState(State::STOPPED); |
| - for (auto& observer : observer_list_) |
| - observer.OnShutdownBridge(); |
| -} |
| - |
| -void ArcAuthService::AddObserver(Observer* observer) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - observer_list_.AddObserver(observer); |
| -} |
| - |
| -void ArcAuthService::RemoveObserver(Observer* observer) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - observer_list_.RemoveObserver(observer); |
| -} |
| - |
| -// This is the special method to support enterprise mojo API. |
| -// TODO(hidehiko): Remove this. |
| -void ArcAuthService::StopAndEnableArc() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK(!arc_bridge_service()->stopped()); |
| - reenable_arc_ = true; |
| - StopArc(); |
| -} |
| - |
| -void ArcAuthService::StartArc() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - arc_bridge_service()->RequestStart(); |
| - SetState(State::ACTIVE); |
| -} |
| - |
| void ArcAuthService::OnAuthCodeObtained(const std::string& auth_code) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(!auth_code.empty()); |
| - account_info_notifier_->Notify(!IsOptInVerificationDisabled(), auth_code, |
| - GetAccountType(), |
| - policy_util::IsAccountManaged(profile_)); |
| - account_info_notifier_.reset(); |
| -} |
| - |
| -void ArcAuthService::OnArcSignInTimeout() { |
| - LOG(ERROR) << "Timed out waiting for first sign in."; |
| - OnProvisioningFinished(ProvisioningResult::OVERALL_SIGN_IN_TIMEOUT); |
| -} |
| - |
| -void ArcAuthService::CancelAuthCode() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - |
| - if (state_ == State::NOT_INITIALIZED) { |
| - NOTREACHED(); |
| - return; |
| - } |
| - |
| - // In case |state_| is ACTIVE, UI page can be ARC_LOADING (which means normal |
| - // ARC booting) or ERROR (in case ARC can not be started). If ARC is booting |
| - // normally don't stop it on progress close. |
| - if ((state_ != State::SHOWING_TERMS_OF_SERVICE && |
| - state_ != State::CHECKING_ANDROID_MANAGEMENT) && |
| - (!support_host_ || |
| - support_host_->ui_page() != ArcSupportHost::UIPage::ERROR)) { |
| - return; |
| - } |
| - |
| - // Update UMA with user cancel only if error is not currently shown. |
| - if (support_host_ && |
| - support_host_->ui_page() != ArcSupportHost::UIPage::NO_PAGE && |
| - support_host_->ui_page() != ArcSupportHost::UIPage::ERROR) { |
| - UpdateOptInCancelUMA(OptInCancelReason::USER_CANCEL); |
| - } |
| - |
| - StopArc(); |
| - |
| - if (IsArcManaged()) |
| - return; |
| - |
| - DisableArc(); |
| -} |
| - |
| -bool ArcAuthService::IsArcManaged() const { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK(profile_); |
| - return profile_->GetPrefs()->IsManagedPreference(prefs::kArcEnabled); |
| -} |
| - |
| -bool ArcAuthService::IsArcEnabled() const { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - if (!IsAllowed()) |
| - return false; |
| - |
| - DCHECK(profile_); |
| - return profile_->GetPrefs()->GetBoolean(prefs::kArcEnabled); |
| -} |
| - |
| -void ArcAuthService::EnableArc() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK(profile_); |
| - |
| - if (IsArcEnabled()) { |
| - OnOptInPreferenceChanged(); |
| - return; |
| - } |
| - |
| - if (!IsArcManaged()) |
| - profile_->GetPrefs()->SetBoolean(prefs::kArcEnabled, true); |
| -} |
| - |
| -void ArcAuthService::DisableArc() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK(profile_); |
| - profile_->GetPrefs()->SetBoolean(prefs::kArcEnabled, false); |
| -} |
| - |
| -void ArcAuthService::RecordArcState() { |
| - // Only record Enabled state if ARC is allowed in the first place, so we do |
| - // not split the ARC population by devices that cannot run ARC. |
| - if (IsAllowed()) |
| - UpdateEnabledStateUMA(IsArcEnabled()); |
| -} |
| - |
| -void ArcAuthService::StartUI() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - |
| - if (!arc_bridge_service()->stopped()) { |
| - // If the user attempts to re-enable ARC while the bridge is still running |
| - // the user should not be able to continue until the bridge has stopped. |
| - if (support_host_) { |
| - support_host_->ShowError( |
| - ArcSupportHost::Error::SIGN_IN_SERVICE_UNAVAILABLE_ERROR, false); |
| - } |
| - return; |
| - } |
| - |
| - SetState(State::SHOWING_TERMS_OF_SERVICE); |
| - if (support_host_) |
| - support_host_->ShowTermsOfService(); |
| -} |
| - |
| -void ArcAuthService::StartArcAndroidManagementCheck() { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK(arc_bridge_service()->stopped()); |
| - DCHECK(state_ == State::SHOWING_TERMS_OF_SERVICE || |
| - state_ == State::CHECKING_ANDROID_MANAGEMENT); |
| - SetState(State::CHECKING_ANDROID_MANAGEMENT); |
| - |
| - android_management_checker_.reset(new ArcAndroidManagementChecker( |
| - profile_, context_->token_service(), context_->account_id(), |
| - false /* retry_on_error */)); |
| - android_management_checker_->StartCheck( |
| - base::Bind(&ArcAuthService::OnAndroidManagementChecked, |
| - weak_ptr_factory_.GetWeakPtr())); |
| -} |
| - |
| -void ArcAuthService::OnAndroidManagementChecked( |
| - policy::AndroidManagementClient::Result result) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - DCHECK_EQ(state_, State::CHECKING_ANDROID_MANAGEMENT); |
| - |
| - switch (result) { |
| - case policy::AndroidManagementClient::Result::UNMANAGED: |
| - VLOG(1) << "Starting ARC for first sign in."; |
| - sign_in_time_ = base::Time::Now(); |
| - arc_sign_in_timer_.Start(FROM_HERE, kArcSignInTimeout, |
| - base::Bind(&ArcAuthService::OnArcSignInTimeout, |
| - weak_ptr_factory_.GetWeakPtr())); |
| - StartArc(); |
| - break; |
| - case policy::AndroidManagementClient::Result::MANAGED: |
| - ShutdownBridge(); |
| - if (support_host_) { |
| - support_host_->ShowError( |
| - ArcSupportHost::Error::ANDROID_MANAGEMENT_REQUIRED_ERROR, false); |
| - } |
| - UpdateOptInCancelUMA(OptInCancelReason::ANDROID_MANAGEMENT_REQUIRED); |
| - break; |
| - case policy::AndroidManagementClient::Result::ERROR: |
| - ShutdownBridge(); |
| - if (support_host_) { |
| - support_host_->ShowError( |
| - ArcSupportHost::Error::SERVER_COMMUNICATION_ERROR, false); |
| - } |
| - UpdateOptInCancelUMA(OptInCancelReason::NETWORK_ERROR); |
| - break; |
| - } |
| -} |
| - |
| -void ArcAuthService::OnBackgroundAndroidManagementChecked( |
| - policy::AndroidManagementClient::Result result) { |
| - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| - switch (result) { |
| - case policy::AndroidManagementClient::Result::UNMANAGED: |
| - // Do nothing. ARC should be started already. |
| - break; |
| - case policy::AndroidManagementClient::Result::MANAGED: |
| - DisableArc(); |
| - break; |
| - case policy::AndroidManagementClient::Result::ERROR: |
| - // This code should not be reached. For background check, |
| - // retry_on_error should be set. |
| - NOTREACHED(); |
| - } |
| -} |
| - |
| -void ArcAuthService::OnWindowClosed() { |
| - DCHECK(support_host_); |
| - CancelAuthCode(); |
| -} |
| - |
| -void ArcAuthService::OnTermsAgreed(bool is_metrics_enabled, |
| - bool is_backup_and_restore_enabled, |
| - bool is_location_service_enabled) { |
| - DCHECK(support_host_); |
| - |
| - // Terms were accepted |
| - profile_->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, true); |
| - |
| - // Since this is ARC support's UI event callback, preference_handler_ |
| - // should be always created (see OnPrimaryUserProfilePrepared()). |
| - // TODO(hidehiko): Simplify the logic with the code restructuring. |
| - DCHECK(preference_handler_); |
| - preference_handler_->EnableMetrics(is_metrics_enabled); |
| - preference_handler_->EnableBackupRestore(is_backup_and_restore_enabled); |
| - preference_handler_->EnableLocationService(is_location_service_enabled); |
| - support_host_->ShowArcLoading(); |
| - StartArcAndroidManagementCheck(); |
| + notifier_->Notify( |
| + !ArcSessionManager::IsOptInVerificationDisabled(), auth_code, |
| + GetAccountType(), |
| + policy_util::IsAccountManaged(ArcSessionManager::Get()->profile())); |
| + notifier_.reset(); |
| } |
| void ArcAuthService::OnAuthSucceeded(const std::string& auth_code) { |
| - DCHECK(support_host_); |
| OnAuthCodeObtained(auth_code); |
| } |
| void ArcAuthService::OnRetryClicked() { |
| - DCHECK(support_host_); |
| - |
| - UpdateOptInActionUMA(OptInActionType::RETRY); |
| - |
| - // TODO(hidehiko): Simplify the retry logic. |
| - if (!profile_->GetPrefs()->GetBoolean(prefs::kArcTermsAccepted)) { |
| - // If the user has not yet agreed on Terms of Service, then show it. |
| - support_host_->ShowTermsOfService(); |
| - } else if (support_host_->ui_page() == ArcSupportHost::UIPage::ERROR && |
| - !arc_bridge_service()->stopped()) { |
| - // ERROR_WITH_FEEDBACK is set in OnSignInFailed(). In the case, stopping |
| - // ARC was postponed to contain its internal state into the report. |
| - // Here, on retry, stop it, then restart. |
| - DCHECK_EQ(State::ACTIVE, state_); |
| - support_host_->ShowArcLoading(); |
| - ShutdownBridge(); |
| - reenable_arc_ = true; |
| - } else if (state_ == State::ACTIVE) { |
| - // This happens when ARC support Chrome app reports an error on "Sign in" |
| - // page. |
| - support_host_->ShowLso(); |
| - } else { |
| - // Otherwise, we restart ARC. Note: this is the first boot case. |
| - // For second or later boot, either ERROR_WITH_FEEDBACK case or ACTIVE |
| - // case must hit. |
| - support_host_->ShowArcLoading(); |
| - StartArcAndroidManagementCheck(); |
| - } |
| -} |
| - |
| -void ArcAuthService::OnSendFeedbackClicked() { |
| - DCHECK(support_host_); |
| - chrome::OpenFeedbackDialog(nullptr); |
| -} |
| - |
| -void ArcAuthService::OnMetricsModeChanged(bool enabled, bool managed) { |
| - if (!support_host_) |
| + ArcSupportHost* support_host = ArcSessionManager::Get()->support_host(); |
| + DCHECK(support_host); |
|
Luis Héctor Chávez
2016/11/16 17:13:34
Please mention this is a callback from the Support
hidehiko
2016/11/16 19:52:05
Done.
|
| + if (support_host->ui_page() == ArcSupportHost::UIPage::ERROR) { |
| + // This case is handled by ArcSessionManager::OnRetryClicked(). |
| return; |
| - support_host_->SetMetricsPreferenceCheckbox(enabled, managed); |
| -} |
| - |
| -void ArcAuthService::OnBackupAndRestoreModeChanged(bool enabled, bool managed) { |
| - if (!support_host_) |
| - return; |
| - support_host_->SetBackupAndRestorePreferenceCheckbox(enabled, managed); |
| -} |
| - |
| -void ArcAuthService::OnLocationServicesModeChanged(bool enabled, bool managed) { |
| - if (!support_host_) |
| - return; |
| - support_host_->SetLocationServicesPreferenceCheckbox(enabled, managed); |
| -} |
| - |
| -std::ostream& operator<<(std::ostream& os, const ArcAuthService::State& state) { |
| - switch (state) { |
| - case ArcAuthService::State::NOT_INITIALIZED: |
| - return os << "NOT_INITIALIZED"; |
| - case ArcAuthService::State::STOPPED: |
| - return os << "STOPPED"; |
| - case ArcAuthService::State::SHOWING_TERMS_OF_SERVICE: |
| - return os << "SHOWING_TERMS_OF_SERVICE"; |
| - case ArcAuthService::State::CHECKING_ANDROID_MANAGEMENT: |
| - return os << "CHECKING_ANDROID_MANAGEMENT"; |
| - case ArcAuthService::State::ACTIVE: |
| - return os << "ACTIVE"; |
| } |
| - |
| - // Some compiler reports an error even if all values of an enum-class are |
| - // covered indivisually in a switch statement. |
| - NOTREACHED(); |
| - return os; |
| + support_host->ShowLso(); |
| } |
| } // namespace arc |