chromium/chrome/browser/password_manager/android/local_passwords_migration_warning_util.cc

// Copyright 2023 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/local_passwords_migration_warning_util.h"

#include "base/android/build_info.h"
#include "base/android/scoped_java_ref.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "components/password_manager/core/browser/features/password_features.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_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/version_info/android/channel_getter.h"
#include "ui/android/window_android.h"
#include "ui/gfx/native_widget_types.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/android/chrome_jni_headers/PasswordMigrationWarningBridge_jni.h"

using base::android::AttachCurrentThread;
using password_manager::prefs::UseUpmLocalAndSeparateStoresState;

namespace local_password_migration {

constexpr base::TimeDelta kMinIntervalBetweenWarnings = base::Days(1);

void SaveWarningShownTimestamp(PrefService* pref_service) {
  pref_service->SetTime(
      password_manager::prefs::kLocalPasswordsMigrationWarningShownTimestamp,
      base::Time::Now());
}

void RecordPasswordMigrationWarningTriggerSource(
    password_manager::metrics_util::PasswordMigrationWarningTriggers
        trigger_source) {
  base::UmaHistogramEnumeration(
      "PasswordManager.PasswordMigrationWarning.Trigger", trigger_source);
}

void ShowWarning(
    const gfx::NativeWindow window,
    Profile* profile,
    password_manager::metrics_util::PasswordMigrationWarningTriggers
        trigger_source) {
  if (!window) {
    return;
  }
  if (!ShouldShowWarning(profile)) {
    return;
  }
  SaveWarningShownTimestamp(profile->GetPrefs());

  Java_PasswordMigrationWarningBridge_showWarning(
      AttachCurrentThread(), window->GetJavaObject(), profile->GetJavaObject(),
      static_cast<int>(trigger_source));

  RecordPasswordMigrationWarningTriggerSource(trigger_source);
}

void ShowWarningWithActivity(
    const base::android::JavaParamRef<jobject>& activity,
    const base::android::JavaParamRef<jobject>& bottom_sheet_controller,
    Profile* profile,
    password_manager::metrics_util::PasswordMigrationWarningTriggers
        trigger_source) {
  if (!ShouldShowWarning(profile)) {
    return;
  }
  SaveWarningShownTimestamp(profile->GetPrefs());

  Java_PasswordMigrationWarningBridge_showWarningWithActivity(
      AttachCurrentThread(), activity, bottom_sheet_controller,
      profile->GetJavaObject(), static_cast<int>(trigger_source));

  RecordPasswordMigrationWarningTriggerSource(trigger_source);
}

bool ShouldShowWarning(Profile* profile) {
  if (base::android::BuildInfo::GetInstance()->is_automotive()) {
    return false;
  }
  // If we're showing the access loss warnings, there is no need to show the
  // migration warning anymore.
  if (base::FeatureList::IsEnabled(
          password_manager::features::
              kUnifiedPasswordManagerLocalPasswordsAndroidAccessLossWarning)) {
    return false;
  }
  if (password_manager::UsesSplitStoresAndUPMForLocal(profile->GetPrefs())) {
    return false;
  }
  // The warning should not show up on stable builds.
  version_info::Channel channel = version_info::android::GetChannel();
  if (channel == version_info::Channel::STABLE) {
    return false;
  }

  if (profile->IsOffTheRecord()) {
    return false;
  }

  if (!base::FeatureList::IsEnabled(
          password_manager::features::
              kUnifiedPasswordManagerLocalPasswordsMigrationWarning)) {
    return false;
  }

  PrefService* pref_service = profile->GetPrefs();
  bool is_warning_acknowledged = pref_service->GetBoolean(
      password_manager::prefs::kUserAcknowledgedLocalPasswordsMigrationWarning);
  if (is_warning_acknowledged) {
    return false;
  }

  if (password_manager::sync_util::HasChosenToSyncPasswords(
          SyncServiceFactory::GetForProfile(profile))) {
    // No signed-in / syncing users with password sync enabled should see the
    // warning. This is an oversimplification to avoid confusion, in reality
    // some users in this group *do* save to LoginDatabase (e.g. if GmsCore is
    // outdated).
    return false;
  }

  if (password_manager::features::kIgnoreMigrationWarningTimeout.Get()) {
    return true;
  }

  base::Time last_shown_timestamp = pref_service->GetTime(
      password_manager::prefs::kLocalPasswordsMigrationWarningShownTimestamp);
  base::TimeDelta time_since_last_shown =
      base::Time::Now() - last_shown_timestamp;
  if (time_since_last_shown < kMinIntervalBetweenWarnings) {
    return false;
  }

  return true;
}

void MaybeShowPostMigrationSheet(const gfx::NativeWindow window,
                                 Profile* profile) {
  if (!window) {
    return;
  }
  if (!profile) {
    return;
  }
  if (!ShouldShowPostMigrationSheet(profile)) {
    return;
  }

  Java_PasswordMigrationWarningBridge_maybeShowPostMigrationSheet(
      AttachCurrentThread(), window->GetJavaObject(), profile->GetJavaObject());
}

bool ShouldShowPostMigrationSheet(Profile* profile) {
  // Don't show in incognito.
  if (profile->IsOffTheRecord()) {
    return false;
  }

  // The sheet should only show on non-stable channels.
  version_info::Channel channel = version_info::android::GetChannel();
  if (channel == version_info::Channel::STABLE) {
    return false;
  }

  if (base::android::BuildInfo::GetInstance()->is_automotive()) {
    return false;
  }

  // The post password migration sheet should be shown for an active UPM user
  // that uses split stores only once, so
  // `kLocalPasswordMigrationWarningShownAtStartup` will be true only when the
  // migration algorithm sets it to true and it will be flipped to false when
  // the sheet is shown.
  return profile->GetPrefs()->GetBoolean(
      password_manager::prefs::kShouldShowPostPasswordMigrationSheetAtStartup);
}

}  // namespace local_password_migration