chromium/ios/chrome/browser/passwords/model/ios_password_store_utils.mm

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

#import "ios/chrome/browser/passwords/model/ios_password_store_utils.h"

#import <AuthenticationServices/AuthenticationServices.h>

#import "base/functional/bind.h"
#import "base/memory/raw_ptr.h"
#import "base/metrics/histogram_functions.h"
#import "base/task/sequenced_task_runner.h"
#import "components/keyed_service/core/service_access_type.h"
#import "components/password_manager/core/browser/password_manager_util.h"
#import "components/password_manager/core/browser/password_store/password_store_interface.h"
#import "components/password_manager/core/browser/store_metrics_reporter.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_account_password_store_factory.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_password_reuse_manager_factory.h"
#import "ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.h"
#import "ios/chrome/browser/passwords/model/ios_password_manager_settings_service_factory.h"
#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"

namespace password_manager {
class PasswordReuseManager;
}

namespace {

// Used for attaching metrics reporter to a WebContents.
constexpr char kPasswordStoreMetricsReporterKey[] =
    "PasswordStoreMetricsReporterKey";

class StoreMetricReporterHelper : public base::SupportsUserData::Data {
 public:
  explicit StoreMetricReporterHelper(ChromeBrowserState* browser_state)
      : browser_state_(browser_state) {
    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
        FROM_HERE,
        base::BindOnce(&StoreMetricReporterHelper::StartMetricsReporting,
                       weak_ptr_factory_.GetWeakPtr()),
        base::Seconds(30));
  }
  ~StoreMetricReporterHelper() override = default;

 private:
  void StartMetricsReporting() {
    password_manager::PasswordStoreInterface* profile_store =
        IOSChromeProfilePasswordStoreFactory::GetForBrowserState(
            browser_state_, ServiceAccessType::EXPLICIT_ACCESS)
            .get();
    password_manager::PasswordStoreInterface* account_store =
        IOSChromeAccountPasswordStoreFactory::GetForBrowserState(
            browser_state_, ServiceAccessType::EXPLICIT_ACCESS)
            .get();
    syncer::SyncService* sync_service =
        SyncServiceFactory::GetForBrowserStateIfExists(browser_state_);
    password_manager::PasswordReuseManager* password_reuse_manager =
        IOSChromePasswordReuseManagerFactory::GetForBrowserState(
            browser_state_);
    password_manager::PasswordManagerSettingsService* settings =
        IOSPasswordManagerSettingsServiceFactory::GetForBrowserState(
            browser_state_);

    PrefService* pref_service = browser_state_->GetPrefs();

    metrics_reporter_ = std::make_unique<
        password_manager::StoreMetricsReporter>(
        profile_store, account_store, sync_service, pref_service,
        password_reuse_manager, settings,
        base::BindOnce(
            &StoreMetricReporterHelper::RemoveInstanceFromBrowserStateUserData,
            weak_ptr_factory_.GetWeakPtr()));

    [ASCredentialIdentityStore.sharedStore
        getCredentialIdentityStoreStateWithCompletion:^(
            ASCredentialIdentityStoreState* state) {
          // The completion handler sent to ASCredentialIdentityStore is
          // executed on a background thread. Putting it back onto the main
          // thread to handle the logging that requires access to the Chrome
          // browser state.
          dispatch_async(dispatch_get_main_queue(), ^{
            BOOL enabled = state.isEnabled;
            LogIfCredentialProviderEnabled(pref_service, enabled);
          });
        }];
  }

  void RemoveInstanceFromBrowserStateUserData() {
    browser_state_->RemoveUserData(kPasswordStoreMetricsReporterKey);
  }

  // Logs if the user had enabled the credential provider in their iOS settings
  // at startup. Also if the value has changed since the last launch, log the
  // new value.
  void LogIfCredentialProviderEnabled(PrefService* pref_service, BOOL enabled) {
    base::UmaHistogramBoolean("IOS.CredentialExtension.IsEnabled.Startup",
                              enabled);
    if (pref_service) {
      // The value stored on the last app startup.
      bool is_credential_provider_enabled =
          password_manager_util::IsCredentialProviderEnabledOnStartup(
              pref_service);
      // If the value changed since last launch, store the new value and log
      // that the value has changed.
      if (enabled != is_credential_provider_enabled) {
        password_manager_util::SetCredentialProviderEnabledOnStartup(
            pref_service, enabled);
        base::UmaHistogramBoolean(
            "IOS.CredentialExtension.StatusDidChangeTo.Startup", enabled);
      }
    }
  }

  const raw_ptr<ChromeBrowserState> browser_state_;
  // StoreMetricReporterHelper is owned by the profile `metrics_reporter_` life
  // time is now bound to the profile.
  std::unique_ptr<password_manager::StoreMetricsReporter> metrics_reporter_;
  base::WeakPtrFactory<StoreMetricReporterHelper> weak_ptr_factory_{this};
};

}  // namespace

void DelayReportingPasswordStoreMetrics(ChromeBrowserState* browser_state) {
  browser_state->SetUserData(
      kPasswordStoreMetricsReporterKey,
      std::make_unique<StoreMetricReporterHelper>(browser_state));
}