// Copyright 2024 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/password_manager/android/password_store_android_account_backend.h"
#include "base/android/build_info.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/password_manager/android/password_manager_eviction_util.h"
#include "chrome/browser/password_manager/android/password_manager_lifecycle_helper_impl.h"
#include "chrome/browser/password_manager/android/password_sync_controller_delegate_android.h"
#include "chrome/browser/password_manager/android/password_sync_controller_delegate_bridge_impl.h"
#include "components/password_manager/core/browser/affiliation/affiliated_match_helper.h"
#include "components/password_manager/core/browser/affiliation/password_affiliation_source_adapter.h"
#include "components/password_manager/core/browser/features/password_features.h"
#include "components/password_manager/core/browser/password_store/get_logins_with_affiliations_request_handler.h"
#include "components/password_manager/core/browser/password_store/password_data_type_controller_delegate_android.h"
#include "components/password_manager/core/browser/password_store/password_store_backend_error.h"
#include "components/password_manager/core/browser/password_store/password_store_backend_metrics_recorder.h"
#include "components/password_manager/core/browser/password_sync_util.h"
#include "components/password_manager/core/browser/split_stores_and_local_upm.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/sync/base/features.h"
#include "components/sync/service/sync_service.h"
namespace password_manager {
namespace {
constexpr char kUPMActiveHistogram[] =
"PasswordManager.UnifiedPasswordManager.ActiveStatus2";
std::string GetSyncingAccount(const syncer::SyncService* sync_service) {
CHECK(sync_service);
return password_manager::sync_util::HasChosenToSyncPasswords(sync_service)
? sync_service->GetAccountInfo().email
: std::string();
}
void LogUPMActiveStatus(syncer::SyncService* sync_service, PrefService* prefs) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service)) {
base::UmaHistogramEnumeration(
kUPMActiveHistogram,
UnifiedPasswordManagerActiveStatus::kInactiveSyncOff);
return;
}
if (password_manager_upm_eviction::IsCurrentUserEvicted(prefs)) {
base::UmaHistogramEnumeration(
kUPMActiveHistogram,
UnifiedPasswordManagerActiveStatus::kInactiveUnenrolledDueToErrors);
return;
}
base::UmaHistogramEnumeration(kUPMActiveHistogram,
UnifiedPasswordManagerActiveStatus::kActive);
}
template <typename Response, typename CallbackType>
void ReplyWithEmptyList(CallbackType callback) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), Response()));
}
} // namespace
PasswordStoreAndroidAccountBackend::PasswordStoreAndroidAccountBackend(
PrefService* prefs,
PasswordAffiliationSourceAdapter* password_affiliation_adapter,
password_manager::IsAccountStore is_account_store)
: PasswordStoreAndroidBackend(
PasswordStoreAndroidBackendBridgeHelper::Create(is_account_store),
std::make_unique<PasswordManagerLifecycleHelperImpl>(),
prefs),
password_affiliation_adapter_(password_affiliation_adapter) {
sync_controller_delegate_ =
std::make_unique<PasswordSyncControllerDelegateAndroid>(
std::make_unique<PasswordSyncControllerDelegateBridgeImpl>());
sync_controller_delegate_->SetSyncObserverCallbacks(
base::BindRepeating(
&PasswordStoreAndroidAccountBackend::OnPasswordsSyncStateChanged,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&PasswordStoreAndroidAccountBackend::SyncShutdown,
weak_ptr_factory_.GetWeakPtr()));
}
PasswordStoreAndroidAccountBackend::PasswordStoreAndroidAccountBackend(
base::PassKey<class PasswordStoreAndroidAccountBackendTest> key,
std::unique_ptr<PasswordStoreAndroidBackendBridgeHelper> bridge_helper,
std::unique_ptr<PasswordManagerLifecycleHelper> lifecycle_helper,
std::unique_ptr<PasswordSyncControllerDelegateAndroid>
sync_controller_delegate,
PrefService* prefs,
PasswordAffiliationSourceAdapter* password_affiliation_adapter)
: PasswordStoreAndroidBackend(std::move(bridge_helper),
std::move(lifecycle_helper),
prefs),
password_affiliation_adapter_(password_affiliation_adapter) {
sync_controller_delegate_ = std::move(sync_controller_delegate);
sync_controller_delegate_->SetSyncObserverCallbacks(
base::BindRepeating(
&PasswordStoreAndroidAccountBackend::OnPasswordsSyncStateChanged,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&PasswordStoreAndroidAccountBackend::SyncShutdown,
weak_ptr_factory_.GetWeakPtr()));
}
PasswordStoreAndroidAccountBackend::~PasswordStoreAndroidAccountBackend() =
default;
void PasswordStoreAndroidAccountBackend::InitBackend(
AffiliatedMatchHelper* affiliated_match_helper,
RemoteChangesReceived remote_form_changes_received,
base::RepeatingClosure sync_enabled_or_disabled_cb,
base::OnceCallback<void(bool)> completion) {
Init(std::move(remote_form_changes_received));
CHECK(completion);
affiliated_match_helper_ = affiliated_match_helper;
sync_enabled_or_disabled_cb_ = std::move(sync_enabled_or_disabled_cb);
std::move(completion).Run(/*success*/ true);
}
void PasswordStoreAndroidAccountBackend::Shutdown(
base::OnceClosure shutdown_completed) {
weak_ptr_factory_.InvalidateWeakPtrs();
affiliated_match_helper_ = nullptr;
sync_service_ = nullptr;
PasswordStoreAndroidBackend::Shutdown(std::move(shutdown_completed));
}
bool PasswordStoreAndroidAccountBackend::IsAbleToSavePasswords() {
base::UmaHistogramBoolean(
"PasswordManager.PasswordSavingDisabledDueToGMSCoreError",
should_disable_saving_due_to_error_);
return sync_service_ != nullptr && !should_disable_saving_due_to_error_;
}
void PasswordStoreAndroidAccountBackend::GetAllLoginsAsync(
LoginsOrErrorReply callback) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service_)) {
ReplyWithEmptyList<LoginsResult>(std::move(callback));
return;
}
GetAllLoginsInternal(GetSyncingAccount(sync_service_), std::move(callback));
}
void PasswordStoreAndroidAccountBackend::
GetAllLoginsWithAffiliationAndBrandingAsync(LoginsOrErrorReply callback) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service_)) {
ReplyWithEmptyList<LoginsResult>(std::move(callback));
return;
}
if (bridge_helper()->CanUseGetAllLoginsWithBrandingInfoAPI()) {
GetAllLoginsWithAffiliationAndBrandingInternal(
GetSyncingAccount(sync_service_), std::move(callback));
return;
}
auto affiliation_injection =
base::BindOnce(&PasswordStoreAndroidAccountBackend::
InjectAffiliationAndBrandingInformation,
weak_ptr_factory_.GetWeakPtr(), std::move(callback));
GetAllLoginsInternal(GetSyncingAccount(sync_service_),
std::move(affiliation_injection));
}
void PasswordStoreAndroidAccountBackend::GetAutofillableLoginsAsync(
LoginsOrErrorReply callback) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service_)) {
ReplyWithEmptyList<LoginsResult>(std::move(callback));
return;
}
GetAutofillableLoginsInternal(GetSyncingAccount(sync_service_),
std::move(callback));
}
void PasswordStoreAndroidAccountBackend::GetAllLoginsForAccountAsync(
std::string account,
LoginsOrErrorReply callback) {
CHECK(!account.empty());
// This method is only used before the store split, to migrate non-syncable
// data back to the built-in backend after password sync turns off.
CHECK(!password_manager::UsesSplitStoresAndUPMForLocal(prefs()));
GetAllLoginsInternal(std::move(account), std::move(callback));
}
void PasswordStoreAndroidAccountBackend::FillMatchingLoginsAsync(
LoginsOrErrorReply callback,
bool include_psl,
const std::vector<PasswordFormDigest>& forms) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service_)) {
ReplyWithEmptyList<LoginsResult>(std::move(callback));
return;
}
FillMatchingLoginsInternal(GetSyncingAccount(sync_service_),
std::move(callback), include_psl, forms);
}
void PasswordStoreAndroidAccountBackend::GetGroupedMatchingLoginsAsync(
const PasswordFormDigest& form_digest,
LoginsOrErrorReply callback) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service_)) {
ReplyWithEmptyList<LoginsResult>(std::move(callback));
return;
}
if (bridge_helper()->CanUseGetAffiliatedPasswordsAPI()) {
GetGroupedMatchingLoginsInternal(GetSyncingAccount(sync_service_),
form_digest, std::move(callback));
return;
}
GetLoginsWithAffiliationsRequestHandler(
form_digest, this, affiliated_match_helper_.get(), std::move(callback));
}
void PasswordStoreAndroidAccountBackend::AddLoginAsync(
const PasswordForm& form,
PasswordChangesOrErrorReply callback) {
CHECK(password_manager::sync_util::HasChosenToSyncPasswords(sync_service_));
AddLoginInternal(GetSyncingAccount(sync_service_), form, std::move(callback));
}
void PasswordStoreAndroidAccountBackend::UpdateLoginAsync(
const PasswordForm& form,
PasswordChangesOrErrorReply callback) {
CHECK(password_manager::sync_util::HasChosenToSyncPasswords(sync_service_));
UpdateLoginInternal(GetSyncingAccount(sync_service_), form,
std::move(callback));
}
void PasswordStoreAndroidAccountBackend::RemoveLoginAsync(
const base::Location& location,
const PasswordForm& form,
PasswordChangesOrErrorReply callback) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service_)) {
ReplyWithEmptyList<PasswordStoreChangeList>(std::move(callback));
return;
}
RemoveLoginInternal(GetSyncingAccount(sync_service_), form,
std::move(callback));
}
void PasswordStoreAndroidAccountBackend::RemoveLoginsByURLAndTimeAsync(
const base::Location& location,
const base::RepeatingCallback<bool(const GURL&)>& url_filter,
base::Time delete_begin,
base::Time delete_end,
base::OnceCallback<void(bool)> sync_completion,
PasswordChangesOrErrorReply callback) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service_)) {
ReplyWithEmptyList<PasswordStoreChangeList>(std::move(callback));
return;
}
RemoveLoginsByURLAndTimeInternal(GetSyncingAccount(sync_service_), url_filter,
delete_begin, delete_end,
std::move(callback));
}
void PasswordStoreAndroidAccountBackend::RemoveLoginsCreatedBetweenAsync(
const base::Location& location,
base::Time delete_begin,
base::Time delete_end,
PasswordChangesOrErrorReply callback) {
if (!password_manager::sync_util::HasChosenToSyncPasswords(sync_service_)) {
ReplyWithEmptyList<PasswordStoreChangeList>(std::move(callback));
return;
}
RemoveLoginsCreatedBetweenInternal(GetSyncingAccount(sync_service_),
delete_begin, delete_end,
std::move(callback));
}
void PasswordStoreAndroidAccountBackend::DisableAutoSignInForOriginsAsync(
const base::RepeatingCallback<bool(const GURL&)>& origin_filter,
base::OnceClosure completion) {
CHECK(password_manager::sync_util::HasChosenToSyncPasswords(sync_service_));
DisableAutoSignInForOriginsInternal(GetSyncingAccount(sync_service_),
origin_filter, std::move(completion));
}
std::unique_ptr<syncer::DataTypeControllerDelegate>
PasswordStoreAndroidAccountBackend::CreateSyncControllerDelegate() {
return std::make_unique<PasswordDataTypeControllerDelegateAndroid>();
}
SmartBubbleStatsStore*
PasswordStoreAndroidAccountBackend::GetSmartBubbleStatsStore() {
return nullptr;
}
base::WeakPtr<PasswordStoreBackend>
PasswordStoreAndroidAccountBackend::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void PasswordStoreAndroidAccountBackend::RecoverOnError(
AndroidBackendAPIErrorCode error) {
CHECK(sync_service_);
if (error == AndroidBackendAPIErrorCode::kPassphraseRequired) {
sync_service_->SendExplicitPassphraseToPlatformClient();
}
should_disable_saving_due_to_error_ = true;
}
void PasswordStoreAndroidAccountBackend::OnCallToGMSCoreSucceeded() {
// Since the API call has succeeded, it's safe to reenable saving.
should_disable_saving_due_to_error_ = false;
}
std::string PasswordStoreAndroidAccountBackend::GetAccountToRetryOperation() {
CHECK(sync_service_);
return GetSyncingAccount(sync_service_);
}
PasswordStoreBackendMetricsRecorder::PasswordStoreAndroidBackendType
PasswordStoreAndroidAccountBackend::GetStorageType() {
return PasswordStoreBackendMetricsRecorder::PasswordStoreAndroidBackendType::
kAccount;
}
void PasswordStoreAndroidAccountBackend::OnSyncServiceInitialized(
syncer::SyncService* sync_service) {
// TODO(crbug.com/40847054) Check if this might be called multiple times
// without a need for it. If it is don't repeatedly initialize the sync
// service to make it clear that it's not needed to do so for future readers
// of the code.
if (!sync_service_) {
LogUPMActiveStatus(sync_service, prefs());
}
sync_service_ = sync_service;
sync_controller_delegate_->OnSyncServiceInitialized(sync_service);
// Stop fetching affiliations if AndroidBackend can be used and branding info
// can be obtained directly from the GMS Core backend.
if (!prefs()->GetBoolean(
prefs::kUnenrolledFromGoogleMobileServicesDueToErrors) &&
password_manager::sync_util::HasChosenToSyncPasswords(sync_service_) &&
bridge_helper()->CanUseGetAllLoginsWithBrandingInfoAPI() &&
password_affiliation_adapter_) {
password_affiliation_adapter_->DisableSource();
}
}
void PasswordStoreAndroidAccountBackend::
RecordAddLoginAsyncCalledFromTheStore() {
base::UmaHistogramBoolean(
"PasswordManager.PasswordStore.AccountBackend.AddLoginCalledOnStore",
true);
}
void PasswordStoreAndroidAccountBackend::
RecordUpdateLoginAsyncCalledFromTheStore() {
base::UmaHistogramBoolean(
"PasswordManager.PasswordStore.AccountBackend.UpdateLoginCalledOnStore",
true);
}
void PasswordStoreAndroidAccountBackend::
InjectAffiliationAndBrandingInformation(
LoginsOrErrorReply callback,
LoginsResultOrError forms_or_error) {
if (!affiliated_match_helper_ ||
absl::holds_alternative<PasswordStoreBackendError>(forms_or_error) ||
absl::get<LoginsResult>(forms_or_error).empty()) {
std::move(callback).Run(std::move(forms_or_error));
return;
}
affiliated_match_helper_->InjectAffiliationAndBrandingInformation(
std::move(absl::get<LoginsResult>(forms_or_error)), std::move(callback));
}
void PasswordStoreAndroidAccountBackend::OnPasswordsSyncStateChanged() {
// Invoke `sync_enabled_or_disabled_cb_` only if M4 feature flag is enabled
// since Chrome no longer actively syncs passwords post M4.
if (sync_enabled_or_disabled_cb_) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, sync_enabled_or_disabled_cb_);
}
// Reply with a recoverable error, because this isn't a persistent issue,
// only a transient state
ClearAllTasksAndReplyWithReason(
AndroidBackendError(
AndroidBackendErrorType::kCancelledPwdSyncStateChanged),
PasswordStoreBackendError(PasswordStoreBackendErrorType::kUncategorized));
}
void PasswordStoreAndroidAccountBackend::SyncShutdown() {
ClearAllTasksAndReplyWithReason(
AndroidBackendError(
AndroidBackendErrorType::kCancelledPwdSyncStateChanged),
PasswordStoreBackendError(PasswordStoreBackendErrorType::kUncategorized));
sync_service_ = nullptr;
}
} // namespace password_manager