| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 
 | // Copyright 2022 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/breadcrumbs/core/breadcrumbs_status.h"
#include <atomic>
#include <optional>
#include "base/command_line.h"
#include "base/rand_util.h"
#include "base/time/time.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/variations/variations_switches.h"
#include "components/version_info/channel.h"
namespace breadcrumbs {
namespace {
// Represents whether breadcrumbs is enabled/disabled from local prefs (the
// default and most common case) or forced to enabled/disabled.
enum class BreadcrumbsEnabledMode {
  kFromLocalPrefs,
  kForceEnabled,
  kForceDisabled
};
// If set to `kFromLocalPrefs`, breadcrumbs is enabled or disabled based on
// Local State prefs like usual. Otherwise, breadcrumbs has been forced either
// on (e.g., for testing) or off (e.g., for Chrome on Android's minimal mode).
std::atomic<BreadcrumbsEnabledMode> breadcrumbs_enabled_mode =
    BreadcrumbsEnabledMode::kFromLocalPrefs;
// The percentage at which breadcrumbs is enabled per channel.
// Enable on most pre-Stable clients to ensure crashes with only a few reports
// have enough breadcrumbs to be useful.
constexpr int kCanaryPercent = 99;
constexpr int kDevPercent = 80;
constexpr int kBetaPercent = 80;
// Enable on a small portion of Stable. This rate is expected to provide enough
// breadcrumbs data while not logging on more clients than necessary.
constexpr int kStablePercent = 5;
// How long logging should be enabled on a client before re-randomizing.
constexpr auto kEnabledDuration = base::Days(30);
// Returns a random boolean representing whether breadcrumbs should be enabled
// for `channel`. For example, if logging is enabled at 5% on `channel`, this
// has a 5% chance of returning true.
bool GetRandomIsEnabled(version_info::Channel channel) {
  int enabled_percent = 0;
  switch (channel) {
    case version_info::Channel::CANARY:
      enabled_percent = kCanaryPercent;
      break;
    case version_info::Channel::DEV:
      enabled_percent = kDevPercent;
      break;
    case version_info::Channel::BETA:
      enabled_percent = kBetaPercent;
      break;
    case version_info::Channel::STABLE:
      enabled_percent = kStablePercent;
      break;
    case version_info::Channel::UNKNOWN:
      break;
  }
  return base::RandInt(1, 100) <= enabled_percent;
}
// Returns true if `prefs` contains both breadcrumbs prefs, and the timestamp is
// valid and newer than `kEnabledDuration`.
bool HasRecentBreadcrumbsPrefs(PrefService* prefs) {
  if (!prefs->HasPrefPath(kEnabledPref) ||
      !prefs->HasPrefPath(kEnabledTimePref)) {
    // Breadcrumbs prefs have never been set.
    return false;
  }
  const auto enabled_time = prefs->GetTime(kEnabledTimePref);
  const auto now = base::Time::Now();
  if (enabled_time > now) {
    // Timestamp is in the future, so consider it invalid.
    return false;
  }
  const auto oldest_valid_time = now - kEnabledDuration;
  return enabled_time > oldest_valid_time;
}
bool IsEnabled(PrefService* prefs,
               std::optional<version_info::Channel> set_for_channel) {
  switch (breadcrumbs_enabled_mode) {
    case BreadcrumbsEnabledMode::kForceEnabled:
      return true;
    case BreadcrumbsEnabledMode::kForceDisabled:
      return false;
    case BreadcrumbsEnabledMode::kFromLocalPrefs:
      // `prefs` can be null or unregistered in tests, and can be null in Chrome
      // for Android's minimal mode. In these cases, breadcrumbs should be
      // disabled for the entire session. Also, breadcrumbs should be disabled
      // while benchmarking, to reflect the typical client experience.
      if (!prefs || !prefs->FindPreference(kEnabledPref) ||
          !prefs->FindPreference(kEnabledTimePref) ||
          base::CommandLine::ForCurrentProcess()->HasSwitch(
              variations::switches::kEnableBenchmarking)) {
        breadcrumbs_enabled_mode = BreadcrumbsEnabledMode::kForceDisabled;
        return false;
      }
      if (set_for_channel.has_value()) {
        // Keep breadcrumbs consistently enabled/disabled on a given client for
        // `kEnabledDuration`. If breadcrumbs has been enabled/disabled in prefs
        // more recently than `kEnabledDuration`, use the existing setting.
        if (HasRecentBreadcrumbsPrefs(prefs)) {
          return prefs->GetBoolean(kEnabledPref);
        }
        // Re-randomize if breadcrumbs was enabled/disabled too long ago. This
        // either enables or disables breadcrumbs for `kEnabledDuration`.
        const bool is_enabled = GetRandomIsEnabled(set_for_channel.value());
        prefs->SetBoolean(kEnabledPref, is_enabled);
        prefs->SetTime(kEnabledTimePref, base::Time::Now());
      }
      return prefs->GetBoolean(kEnabledPref);
  }
}
}  // namespace
constexpr char kEnabledPref[] = "breadcrumbs.enabled";
constexpr char kEnabledTimePref[] = "breadcrumbs.enabled_time";
bool IsEnabled(PrefService* prefs) {
  return IsEnabled(prefs, std::nullopt);
}
bool MaybeEnableBasedOnChannel(PrefService* prefs,
                               version_info::Channel channel) {
  return IsEnabled(prefs, channel);
}
void RegisterPrefs(PrefRegistrySimple* registry) {
  registry->RegisterBooleanPref(kEnabledPref, false);
  registry->RegisterTimePref(kEnabledTimePref, base::Time());
}
ScopedEnableBreadcrumbsForTesting::ScopedEnableBreadcrumbsForTesting() {
  breadcrumbs_enabled_mode = BreadcrumbsEnabledMode::kForceEnabled;
}
ScopedEnableBreadcrumbsForTesting::~ScopedEnableBreadcrumbsForTesting() {
  breadcrumbs_enabled_mode = BreadcrumbsEnabledMode::kFromLocalPrefs;
}
}  // namespace breadcrumbs
 |