chromium/components/account_manager_core/account_manager_facade_impl.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/account_manager_core/account_manager_facade_impl.h"

#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/strings/stringprintf.h"
#include "chromeos/crosapi/mojom/account_manager.mojom.h"
#include "components/account_manager_core/account.h"
#include "components/account_manager_core/account_manager_util.h"
#include "components/account_manager_core/account_upsertion_result.h"
#include "components/account_manager_core/chromeos/account_manager.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "google_apis/gaia/oauth2_access_token_consumer.h"
#include "google_apis/gaia/oauth2_access_token_fetcher.h"
#include "google_apis/gaia/oauth2_access_token_fetcher_immediate_error.h"

namespace account_manager {

namespace {

using RemoteMinVersions = crosapi::mojom::AccountManager::MethodMinVersions;

// UMA histogram names.
const char kAccountUpsertionResultStatus[] =
    "AccountManager.AccountUpsertionResultStatus";
const char kGetAccountsMojoStatus[] =
    "AccountManager.FacadeGetAccountsMojoStatus";
const char kMojoDisconnectionsAccountManagerRemote[] =
    "AccountManager.MojoDisconnections.AccountManagerRemote";
const char kMojoDisconnectionsAccountManagerObserverReceiver[] =
    "AccountManager.MojoDisconnections.AccountManagerObserverReceiver";
const char kMojoDisconnectionsAccountManagerAccessTokenFetcherRemote[] =
    "AccountManager.MojoDisconnections.AccessTokenFetcherRemote";

void UnmarshalAccounts(
    base::OnceCallback<void(const std::vector<Account>&)> callback,
    std::vector<crosapi::mojom::AccountPtr> mojo_accounts) {
  std::vector<Account> accounts;
  for (const auto& mojo_account : mojo_accounts) {
    std::optional<Account> maybe_account = FromMojoAccount(mojo_account);
    if (!maybe_account) {
      // Skip accounts we couldn't unmarshal. No logging, as it would produce
      // a lot of noise.
      continue;
    }
    accounts.emplace_back(std::move(maybe_account.value()));
  }
  std::move(callback).Run(std::move(accounts));
}

void UnmarshalPersistentError(
    base::OnceCallback<void(const GoogleServiceAuthError&)> callback,
    crosapi::mojom::GoogleServiceAuthErrorPtr mojo_error) {
  std::optional<GoogleServiceAuthError> maybe_error =
      FromMojoGoogleServiceAuthError(mojo_error);
  if (!maybe_error) {
    // Couldn't unmarshal GoogleServiceAuthError, report the account as not
    // having an error. This is safe to do, as GetPersistentErrorForAccount is
    // best-effort (there's no way to know that the token was revoked on the
    // server).
    std::move(callback).Run(GoogleServiceAuthError::AuthErrorNone());
    return;
  }
  std::move(callback).Run(maybe_error.value());
}

// Returns whether an account should be available in ARC after it's added
// in-session.
bool GetIsAvailableInArcBySource(
    AccountManagerFacade::AccountAdditionSource source) {
  switch (source) {
    // Accounts added from Ash should be available in ARC.
    case AccountManagerFacade::AccountAdditionSource::kSettingsAddAccountButton:
    case AccountManagerFacade::AccountAdditionSource::
        kAccountManagerMigrationWelcomeScreen:
    case AccountManagerFacade::AccountAdditionSource::kArc:
    case AccountManagerFacade::AccountAdditionSource::kOnboarding:
      return true;
    // Accounts added from the browser should not be available in ARC.
    case AccountManagerFacade::AccountAdditionSource::kChromeProfileCreation:
    case AccountManagerFacade::AccountAdditionSource::kOgbAddAccount:
    case AccountManagerFacade::AccountAdditionSource::
        kAvatarBubbleTurnOnSyncAddAccount:
    case AccountManagerFacade::AccountAdditionSource::
        kChromeExtensionAddAccount:
    case AccountManagerFacade::AccountAdditionSource::
        kChromeSyncPromoAddAccount:
    case AccountManagerFacade::AccountAdditionSource::
        kChromeSettingsTurnOnSyncButton:
    case AccountManagerFacade::AccountAdditionSource::kChromeMenuTurnOnSync:
    case AccountManagerFacade::AccountAdditionSource::
        kChromeSigninPromoAddAccount:
      return false;
    // These are reauthentication cases. ARC visibility shouldn't change for
    // reauthentication.
    case AccountManagerFacade::AccountAdditionSource::kContentAreaReauth:
    case AccountManagerFacade::AccountAdditionSource::
        kSettingsReauthAccountButton:
    case AccountManagerFacade::AccountAdditionSource::
        kAvatarBubbleReauthAccountButton:
    case AccountManagerFacade::AccountAdditionSource::kChromeExtensionReauth:
    case AccountManagerFacade::AccountAdditionSource::kChromeSyncPromoReauth:
    case AccountManagerFacade::AccountAdditionSource::
        kChromeOSProjectorAppReauth:
    case AccountManagerFacade::AccountAdditionSource::
        kChromeSettingsReauthAccountButton:
      NOTREACHED_IN_MIGRATION();
      return false;
    // Unused enums that cannot be deleted.
    case AccountManagerFacade::AccountAdditionSource::kPrintPreviewDialogUnused:
      NOTREACHED_IN_MIGRATION();
      return false;
  }
}

// Error logs the Mojo connection stats when `event` occurs.
void LogMojoConnectionStats(const std::string& event,
                            int num_remote_disconnections,
                            int num_receiver_disconnections) {
  LOG(ERROR) << base::StringPrintf(
      "%s. Number of remote disconnections: %d, "
      "number of receiver disconnections: %d",
      event.c_str(), num_remote_disconnections, num_receiver_disconnections);
}

}  // namespace

// Fetches access tokens over the Mojo remote to `AccountManager`.
class AccountManagerFacadeImpl::AccessTokenFetcher
    : public OAuth2AccessTokenFetcher {
 public:
  AccessTokenFetcher(AccountManagerFacadeImpl* account_manager_facade_impl,
                     const account_manager::AccountKey& account_key,
                     OAuth2AccessTokenConsumer* consumer)
      : OAuth2AccessTokenFetcher(consumer),
        account_manager_facade_impl_(account_manager_facade_impl),
        account_key_(account_key),
        oauth_consumer_name_(consumer->GetConsumerName()) {}

  AccessTokenFetcher(const AccessTokenFetcher&) = delete;
  AccessTokenFetcher& operator=(const AccessTokenFetcher&) = delete;

  ~AccessTokenFetcher() override {
    base::UmaHistogramCounts100(
        kMojoDisconnectionsAccountManagerAccessTokenFetcherRemote,
        num_remote_disconnections_);
  }

  // Returns a closure, which marks `this` instance as ready for use. This
  // happens when `AccountManagerFacadeImpl`'s initialization sequence is
  // complete.
  base::OnceClosure UnblockTokenRequest() {
    return base::BindOnce(&AccessTokenFetcher::UnblockTokenRequestInternal,
                          weak_factory_.GetWeakPtr());
  }

  // Returns a closure which handles Mojo connection errors tied to Account
  // Manager remote.
  base::OnceClosure AccountManagerRemoteDisconnectionClosure() {
    return base::BindOnce(
        &AccessTokenFetcher::OnAccountManagerRemoteDisconnection,
        weak_factory_.GetWeakPtr());
  }

  // OAuth2AccessTokenFetcher override:
  // Note: This implementation ignores `client_id` and `client_secret` because
  // AccountManager's Mojo API does not support overriding OAuth client id and
  // secret.
  void Start(const std::string& client_id,
             const std::string& client_secret,
             const std::vector<std::string>& scopes) override {
    DCHECK(!is_request_pending_);
    is_request_pending_ = true;
    scopes_ = scopes;
    if (!are_token_requests_allowed_) {
      return;
    }
    StartInternal();
  }

  // OAuth2AccessTokenFetcher override:
  void CancelRequest() override {
    access_token_fetcher_.reset();
    is_request_pending_ = false;
  }

 private:
  void UnblockTokenRequestInternal() {
    are_token_requests_allowed_ = true;
    if (is_request_pending_) {
      StartInternal();
    }
  }

  void StartInternal() {
    DCHECK(are_token_requests_allowed_);
    bool is_remote_connected =
        account_manager_facade_impl_->CreateAccessTokenFetcher(
            account_manager::ToMojoAccountKey(account_key_),
            oauth_consumer_name_,
            base::BindOnce(&AccessTokenFetcher::FetchAccessToken,
                           weak_factory_.GetWeakPtr()));

    if (!is_remote_connected) {
      OnAccountManagerRemoteDisconnection();
    }
  }

  void FetchAccessToken(
      mojo::PendingRemote<crosapi::mojom::AccessTokenFetcher> pending_remote) {
    access_token_fetcher_.Bind(std::move(pending_remote));
    access_token_fetcher_.set_disconnect_handler(base::BindOnce(
        &AccessTokenFetcher::OnAccessTokenFetcherRemoteDisconnection,
        weak_factory_.GetWeakPtr()));
    access_token_fetcher_->Start(
        scopes_, base::BindOnce(&AccessTokenFetcher::OnAccessTokenFetchComplete,
                                weak_factory_.GetWeakPtr()));
  }

  void OnAccessTokenFetchComplete(crosapi::mojom::AccessTokenResultPtr result) {
    DCHECK(is_request_pending_);
    is_request_pending_ = false;

    if (result->is_error()) {
      std::optional<GoogleServiceAuthError> maybe_error =
          account_manager::FromMojoGoogleServiceAuthError(result->get_error());

      if (!maybe_error.has_value()) {
        LOG(ERROR) << "Unable to parse error result of access token fetch: "
                   << result->get_error()->state;
        FireOnGetTokenFailure(
            GoogleServiceAuthError::FromUnexpectedServiceResponse(
                "Error parsing Mojo error result of access token fetch"));
      } else {
        FireOnGetTokenFailure(maybe_error.value());
      }
      return;
    }

    FireOnGetTokenSuccess(
        OAuth2AccessTokenConsumer::TokenResponse::Builder()
            .WithAccessToken(result->get_access_token_info()->access_token)
            .WithExpirationTime(
                result->get_access_token_info()->expiration_time)
            .WithIdToken(result->get_access_token_info()->id_token)
            .build());
  }

  void OnAccountManagerRemoteDisconnection() {
    FailPendingRequestWithServiceError("Mojo pipe disconnected");
  }

  void OnAccessTokenFetcherRemoteDisconnection() {
    num_remote_disconnections_++;
    LOG(ERROR) << "Access token fetcher remote disconnected";
    FailPendingRequestWithServiceError("Access token Mojo pipe disconnected");
  }

  void FailPendingRequestWithServiceError(const std::string& message) {
    if (!is_request_pending_)
      return;

    CancelRequest();
    FireOnGetTokenFailure(GoogleServiceAuthError::FromServiceError(message));
  }

  const raw_ptr<AccountManagerFacadeImpl> account_manager_facade_impl_;
  const account_manager::AccountKey account_key_;
  const std::string oauth_consumer_name_;

  bool are_token_requests_allowed_ = false;
  bool is_request_pending_ = false;
  // Number of Mojo pipe disconnections seen by `access_token_fetcher_`.
  int num_remote_disconnections_ = 0;
  std::vector<std::string> scopes_;
  mojo::Remote<crosapi::mojom::AccessTokenFetcher> access_token_fetcher_;

  base::WeakPtrFactory<AccessTokenFetcher> weak_factory_{this};
};

AccountManagerFacadeImpl::AccountManagerFacadeImpl(
    mojo::Remote<crosapi::mojom::AccountManager> account_manager_remote,
    uint32_t remote_version,
    base::WeakPtr<AccountManager> account_manager_for_tests,
    base::OnceClosure init_finished)
    : remote_version_(remote_version),
      account_manager_remote_(std::move(account_manager_remote)),
      account_manager_for_tests_(std::move(account_manager_for_tests)) {
  DCHECK(init_finished);
  initialization_callbacks_.emplace_back(std::move(init_finished));

  if (!account_manager_remote_ ||
      remote_version_ < RemoteMinVersions::kGetAccountsMinVersion) {
    LOG(WARNING) << "Found remote at: " << remote_version_
                 << ", expected: " << RemoteMinVersions::kGetAccountsMinVersion
                 << ". Account consistency will be disabled";
    FinishInitSequenceIfNotAlreadyFinished();
    return;
  }

  account_manager_remote_.set_disconnect_handler(base::BindOnce(
      &AccountManagerFacadeImpl::OnAccountManagerRemoteDisconnected,
      weak_factory_.GetWeakPtr()));
  account_manager_remote_->AddObserver(
      base::BindOnce(&AccountManagerFacadeImpl::OnReceiverReceived,
                     weak_factory_.GetWeakPtr()));
}

AccountManagerFacadeImpl::~AccountManagerFacadeImpl() {
  base::UmaHistogramCounts100(kMojoDisconnectionsAccountManagerRemote,
                              num_remote_disconnections_);
  base::UmaHistogramCounts100(kMojoDisconnectionsAccountManagerObserverReceiver,
                              num_receiver_disconnections_);
}

void AccountManagerFacadeImpl::AddObserver(Observer* observer) {
  observer_list_.AddObserver(observer);
}

void AccountManagerFacadeImpl::RemoveObserver(Observer* observer) {
  observer_list_.RemoveObserver(observer);
}

void AccountManagerFacadeImpl::GetAccounts(
    base::OnceCallback<void(const std::vector<Account>&)> callback) {
  // Record the status of the mojo connection, to get more information about
  // https://crbug.com/1287297
  FacadeMojoStatus mojo_status = FacadeMojoStatus::kOk;
  if (!account_manager_remote_)
    mojo_status = FacadeMojoStatus::kNoRemote;
  else if (remote_version_ < RemoteMinVersions::kGetAccountsMinVersion)
    mojo_status = FacadeMojoStatus::kVersionMismatch;
  else if (!is_initialized_)
    mojo_status = FacadeMojoStatus::kUninitialized;
  base::UmaHistogramEnumeration(kGetAccountsMojoStatus, mojo_status);

  if (!account_manager_remote_ ||
      remote_version_ < RemoteMinVersions::kGetAccountsMinVersion) {
    // Remote side is disconnected or doesn't support GetAccounts. Do not return
    // an empty list as that may cause Lacros to delete user profiles.
    // TODO(crbug.com/40211181): Try to reconnect, or return an error.
    return;
  }
  RunAfterInitializationSequence(
      base::BindOnce(&AccountManagerFacadeImpl::GetAccountsInternal,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void AccountManagerFacadeImpl::GetPersistentErrorForAccount(
    const AccountKey& account,
    base::OnceCallback<void(const GoogleServiceAuthError&)> callback) {
  if (!account_manager_remote_ ||
      remote_version_ <
          RemoteMinVersions::kGetPersistentErrorForAccountMinVersion) {
    // Remote side doesn't support GetPersistentErrorForAccount.
    std::move(callback).Run(GoogleServiceAuthError::AuthErrorNone());
    return;
  }
  RunAfterInitializationSequence(
      base::BindOnce(&AccountManagerFacadeImpl::GetPersistentErrorInternal,
                     weak_factory_.GetWeakPtr(), account, std::move(callback)));
}

void AccountManagerFacadeImpl::ShowAddAccountDialog(
    AccountAdditionSource source) {
  ShowAddAccountDialog(source, base::DoNothing());
}

void AccountManagerFacadeImpl::ShowAddAccountDialog(
    AccountAdditionSource source,
    base::OnceCallback<
        void(const account_manager::AccountUpsertionResult& result)> callback) {
  if (!account_manager_remote_) {
    LOG(WARNING) << "Account Manager remote disconnected";
    FinishUpsertAccount(
        std::move(callback),
        AccountUpsertionResult::FromStatus(
            AccountUpsertionResult::Status::kMojoRemoteDisconnected));
    return;
  }

  if (remote_version_ < RemoteMinVersions::kShowAddAccountDialogMinVersion) {
    LOG(WARNING) << "Found remote at: " << remote_version_ << ", expected: "
                 << RemoteMinVersions::kShowAddAccountDialogMinVersion
                 << " for ShowAddAccountDialog.";
    FinishUpsertAccount(
        std::move(callback),
        AccountUpsertionResult::FromStatus(
            AccountUpsertionResult::Status::kIncompatibleMojoVersions));
    return;
  }

  base::UmaHistogramEnumeration(kAccountAdditionSource, source);

  crosapi::mojom::AccountAdditionOptionsPtr options =
      crosapi::mojom::AccountAdditionOptions::New();
  options->is_available_in_arc = GetIsAvailableInArcBySource(source);
  options->show_arc_availability_picker =
      (source == AccountManagerFacade::AccountAdditionSource::kArc);

  account_manager_remote_->ShowAddAccountDialog(
      std::move(options),
      base::BindOnce(&AccountManagerFacadeImpl::OnSigninDialogActionFinished,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void AccountManagerFacadeImpl::ShowReauthAccountDialog(
    AccountAdditionSource source,
    const std::string& email,
    base::OnceCallback<
        void(const account_manager::AccountUpsertionResult& result)> callback) {
  if (!account_manager_remote_) {
    LOG(WARNING) << "Account Manager remote disconnected";
    FinishUpsertAccount(
        std::move(callback),
        AccountUpsertionResult::FromStatus(
            AccountUpsertionResult::Status::kMojoRemoteDisconnected));
    return;
  }

  if (remote_version_ < RemoteMinVersions::kShowReauthAccountDialogMinVersion) {
    LOG(WARNING) << "Found remote at: " << remote_version_ << ", expected: "
                 << RemoteMinVersions::kShowReauthAccountDialogMinVersion
                 << " for ShowReauthAccountDialog.";
    FinishUpsertAccount(
        std::move(callback),
        AccountUpsertionResult::FromStatus(
            AccountUpsertionResult::Status::kIncompatibleMojoVersions));
    return;
  }

  base::UmaHistogramEnumeration(kAccountAdditionSource, source);

  account_manager_remote_->ShowReauthAccountDialog(
      email,
      base::BindOnce(&AccountManagerFacadeImpl::OnSigninDialogActionFinished,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void AccountManagerFacadeImpl::ShowManageAccountsSettings() {
  if (!account_manager_remote_ ||
      remote_version_ <
          RemoteMinVersions::kShowManageAccountsSettingsMinVersion) {
    LOG(WARNING) << "Found remote at: " << remote_version_ << ", expected: "
                 << RemoteMinVersions::kShowManageAccountsSettingsMinVersion
                 << " for ShowManageAccountsSettings.";
    return;
  }

  account_manager_remote_->ShowManageAccountsSettings();
}

std::unique_ptr<OAuth2AccessTokenFetcher>
AccountManagerFacadeImpl::CreateAccessTokenFetcher(
    const AccountKey& account,
    OAuth2AccessTokenConsumer* consumer) {
  if (!account_manager_remote_ ||
      remote_version_ <
          RemoteMinVersions::kCreateAccessTokenFetcherMinVersion) {
    VLOG(1) << "Found remote at: " << remote_version_ << ", expected: "
            << RemoteMinVersions::kCreateAccessTokenFetcherMinVersion
            << " for CreateAccessTokenFetcher";
    return std::make_unique<OAuth2AccessTokenFetcherImmediateError>(
        consumer,
        GoogleServiceAuthError::FromServiceError("Mojo pipe disconnected"));
  }

  auto access_token_fetcher = std::make_unique<AccessTokenFetcher>(
      /*account_manager_facade_impl=*/this, account, consumer);
  RunAfterInitializationSequence(access_token_fetcher->UnblockTokenRequest());
  RunOnAccountManagerRemoteDisconnection(
      access_token_fetcher->AccountManagerRemoteDisconnectionClosure());
  return std::move(access_token_fetcher);
}

void AccountManagerFacadeImpl::ReportAuthError(
    const account_manager::AccountKey& account,
    const GoogleServiceAuthError& error) {
  if (!account_manager_remote_ ||
      remote_version_ < RemoteMinVersions::kReportAuthErrorMinVersion) {
    LOG(WARNING) << "Found remote at: " << remote_version_ << ", expected: "
                 << RemoteMinVersions::kReportAuthErrorMinVersion
                 << " for ReportAuthError.";
    return;
  }

  account_manager_remote_->ReportAuthError(ToMojoAccountKey(account),
                                           ToMojoGoogleServiceAuthError(error));
}

void AccountManagerFacadeImpl::UpsertAccountForTesting(
    const Account& account,
    const std::string& token_value) {
  CHECK(account_manager_for_tests_);
  account_manager_for_tests_->UpsertAccount(account.key, account.raw_email,
                                            token_value);
}

void AccountManagerFacadeImpl::RemoveAccountForTesting(
    const AccountKey& account) {
  CHECK(account_manager_for_tests_);
  account_manager_for_tests_->RemoveAccount(account);
}

// static
std::string AccountManagerFacadeImpl::
    GetAccountUpsertionResultStatusHistogramNameForTesting() {
  return kAccountUpsertionResultStatus;
}

// static
std::string
AccountManagerFacadeImpl::GetAccountsMojoStatusHistogramNameForTesting() {
  return kGetAccountsMojoStatus;
}

void AccountManagerFacadeImpl::OnReceiverReceived(
    mojo::PendingReceiver<AccountManagerObserver> receiver) {
  receiver_ =
      std::make_unique<mojo::Receiver<crosapi::mojom::AccountManagerObserver>>(
          this, std::move(receiver));
  // At this point (`receiver_` exists), we are subscribed to Account Manager.

  receiver_->set_disconnect_handler(base::BindOnce(
      &AccountManagerFacadeImpl::OnAccountManagerObserverReceiverDisconnected,
      weak_factory_.GetWeakPtr()));

  FinishInitSequenceIfNotAlreadyFinished();
}

void AccountManagerFacadeImpl::OnSigninDialogActionFinished(
    base::OnceCallback<
        void(const account_manager::AccountUpsertionResult& result)> callback,
    crosapi::mojom::AccountUpsertionResultPtr mojo_result) {
  std::optional<account_manager::AccountUpsertionResult> result =
      account_manager::FromMojoAccountUpsertionResult(mojo_result);
  if (!result.has_value()) {
    FinishUpsertAccount(
        std::move(callback),
        AccountUpsertionResult::FromStatus(
            AccountUpsertionResult::Status::kUnexpectedResponse));
    return;
  }
  FinishUpsertAccount(std::move(callback), result.value());
}

void AccountManagerFacadeImpl::FinishUpsertAccount(
    base::OnceCallback<
        void(const account_manager::AccountUpsertionResult& result)> callback,
    const account_manager::AccountUpsertionResult& result) {
  base::UmaHistogramEnumeration(kAccountUpsertionResultStatus, result.status());
  std::move(callback).Run(result);
}

void AccountManagerFacadeImpl::OnTokenUpserted(
    crosapi::mojom::AccountPtr account) {
  std::optional<Account> maybe_account = FromMojoAccount(account);
  if (!maybe_account) {
    LOG(WARNING) << "Can't unmarshal account of type: "
                 << account->key->account_type;
    return;
  }
  for (auto& observer : observer_list_) {
    observer.OnAccountUpserted(maybe_account.value());
  }
}

void AccountManagerFacadeImpl::OnAccountRemoved(
    crosapi::mojom::AccountPtr account) {
  std::optional<Account> maybe_account = FromMojoAccount(account);
  if (!maybe_account) {
    LOG(WARNING) << "Can't unmarshal account of type: "
                 << account->key->account_type;
    return;
  }
  for (auto& observer : observer_list_) {
    observer.OnAccountRemoved(maybe_account.value());
  }
}

void AccountManagerFacadeImpl::OnAuthErrorChanged(
    crosapi::mojom::AccountKeyPtr account,
    crosapi::mojom::GoogleServiceAuthErrorPtr error) {
  std::optional<AccountKey> maybe_account_key = FromMojoAccountKey(account);
  if (!maybe_account_key) {
    LOG(WARNING) << "Can't unmarshal account key of type: "
                 << account->account_type;
    return;
  }

  std::optional<GoogleServiceAuthError> maybe_error =
      FromMojoGoogleServiceAuthError(error);
  if (!maybe_error) {
    LOG(WARNING) << "Can't unmarshal error with state: " << error->state;
    return;
  }

  for (auto& observer : observer_list_) {
    observer.OnAuthErrorChanged(maybe_account_key.value(), maybe_error.value());
  }
}

void AccountManagerFacadeImpl::OnSigninDialogClosed() {
  for (auto& observer : observer_list_) {
    observer.OnSigninDialogClosed();
  }
}

void AccountManagerFacadeImpl::GetAccountsInternal(
    base::OnceCallback<void(const std::vector<Account>&)> callback) {
  account_manager_remote_->GetAccounts(
      base::BindOnce(&UnmarshalAccounts, std::move(callback)));
}

void AccountManagerFacadeImpl::GetPersistentErrorInternal(
    const AccountKey& account,
    base::OnceCallback<void(const GoogleServiceAuthError&)> callback) {
  account_manager_remote_->GetPersistentErrorForAccount(
      ToMojoAccountKey(account),
      base::BindOnce(&UnmarshalPersistentError, std::move(callback)));
}

bool AccountManagerFacadeImpl::CreateAccessTokenFetcher(
    crosapi::mojom::AccountKeyPtr account_key,
    const std::string& oauth_consumer_name,
    crosapi::mojom::AccountManager::CreateAccessTokenFetcherCallback callback) {
  if (!account_manager_remote_) {
    return false;
  }

  account_manager_remote_->CreateAccessTokenFetcher(
      std::move(account_key), oauth_consumer_name, std::move(callback));
  return true;
}

void AccountManagerFacadeImpl::FinishInitSequenceIfNotAlreadyFinished() {
  if (is_initialized_) {
    return;
  }

  is_initialized_ = true;
  for (auto& cb : initialization_callbacks_) {
    std::move(cb).Run();
  }
  initialization_callbacks_.clear();
}

void AccountManagerFacadeImpl::RunAfterInitializationSequence(
    base::OnceClosure closure) {
  if (!is_initialized_) {
    initialization_callbacks_.emplace_back(std::move(closure));
  } else {
    std::move(closure).Run();
  }
}

void AccountManagerFacadeImpl::RunOnAccountManagerRemoteDisconnection(
    base::OnceClosure closure) {
  if (!account_manager_remote_) {
    std::move(closure).Run();
    return;
  }
  account_manager_remote_disconnection_handlers_.emplace_back(
      std::move(closure));
}

void AccountManagerFacadeImpl::OnAccountManagerRemoteDisconnected() {
  num_remote_disconnections_++;
  LogMojoConnectionStats("Account Manager disconnected",
                         num_remote_disconnections_,
                         num_receiver_disconnections_);
  for (auto& cb : account_manager_remote_disconnection_handlers_) {
    std::move(cb).Run();
  }
  account_manager_remote_disconnection_handlers_.clear();
  account_manager_remote_.reset();
}

void AccountManagerFacadeImpl::OnAccountManagerObserverReceiverDisconnected() {
  num_receiver_disconnections_++;
  LogMojoConnectionStats("Account Manager Observer disconnected",
                         num_remote_disconnections_,
                         num_receiver_disconnections_);
}

bool AccountManagerFacadeImpl::IsInitialized() {
  return is_initialized_;
}

void AccountManagerFacadeImpl::FlushMojoForTesting() {
  if (!account_manager_remote_) {
    return;
  }
  account_manager_remote_.FlushForTesting();
}

}  // namespace account_manager