Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VPN enabled duration metric #28020

Merged
merged 1 commit into from
Mar 25, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion browser/brave_browser_main_extra_parts.cc
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
#include "base/metrics/histogram_macros.h"
#include "brave/browser/brave_browser_process_impl.h"
#include "brave/browser/misc_metrics/process_misc_metrics.h"
#include "brave/browser/misc_metrics/uptime_monitor.h"
#include "brave/browser/misc_metrics/uptime_monitor_impl.h"
#include "brave/components/brave_shields/content/browser/brave_shields_p3a.h"
#include "brave/components/p3a/buildflags.h"
#include "brave/components/p3a/p3a_service.h"
4 changes: 2 additions & 2 deletions browser/brave_local_state_prefs.cc
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
#include "brave/browser/metrics/buildflags/buildflags.h"
#include "brave/browser/metrics/metrics_reporting_util.h"
#include "brave/browser/misc_metrics/process_misc_metrics.h"
#include "brave/browser/misc_metrics/uptime_monitor.h"
#include "brave/browser/misc_metrics/uptime_monitor_impl.h"
#include "brave/browser/ntp_background/ntp_p3a_helper_impl.h"
#include "brave/browser/playlist/playlist_service_factory.h"
#include "brave/browser/search_engines/search_engine_tracker.h"
@@ -91,7 +91,7 @@ void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(kDefaultBrowserPromptEnabled, true);
#endif

misc_metrics::UptimeMonitor::RegisterPrefsForMigration(registry);
misc_metrics::UptimeMonitorImpl::RegisterPrefsForMigration(registry);
brave_wallet::RegisterLocalStatePrefsForMigration(registry);
brave_search_conversion::p3a::RegisterLocalStatePrefsForMigration(registry);
brave_stats::RegisterLocalStatePrefsForMigration(registry);
14 changes: 13 additions & 1 deletion browser/brave_vpn/brave_vpn_service_factory.cc
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@
#include "base/no_destructor.h"
#include "brave/browser/brave_browser_process.h"
#include "brave/browser/brave_vpn/vpn_utils.h"
#include "brave/browser/misc_metrics/process_misc_metrics.h"
#include "brave/browser/misc_metrics/uptime_monitor_impl.h"
#include "brave/browser/skus/skus_service_factory.h"
#include "brave/components/brave_vpn/browser/brave_vpn_service.h"
#include "brave/components/brave_vpn/common/brave_vpn_utils.h"
@@ -59,11 +61,21 @@ std::unique_ptr<KeyedService> BuildVpnService(
},
context);

// Get the UptimeMonitor from ProcessMiscMetrics
base::WeakPtr<misc_metrics::UptimeMonitor> uptime_monitor = nullptr;
if (g_brave_browser_process &&
g_brave_browser_process->process_misc_metrics() &&
g_brave_browser_process->process_misc_metrics()->uptime_monitor()) {
uptime_monitor = g_brave_browser_process->process_misc_metrics()
->uptime_monitor()
->GetWeakPtr();
}

std::unique_ptr<BraveVpnService> vpn_service =
std::make_unique<BraveVpnService>(
g_brave_browser_process->brave_vpn_connection_manager(),
shared_url_loader_factory, local_state,
user_prefs::UserPrefs::Get(context), callback);
user_prefs::UserPrefs::Get(context), uptime_monitor, callback);
#if BUILDFLAG(IS_WIN)
vpn_service->set_delegate(std::make_unique<BraveVPNServiceDelegateWin>());
if (auto* wg_observer_service =
4 changes: 2 additions & 2 deletions browser/misc_metrics/BUILD.gn
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ static_library("misc_metrics_impl") {
"//brave/browser/misc_metrics/process_misc_metrics.cc",
"//brave/browser/misc_metrics/profile_misc_metrics_service.h",
"//brave/browser/misc_metrics/profile_misc_metrics_service_factory.h",
"//brave/browser/misc_metrics/uptime_monitor.h",
"//brave/browser/misc_metrics/uptime_monitor_impl.h",
]

deps = [
@@ -85,7 +85,7 @@ source_set("unit_tests") {
sources = [
"doh_metrics_unittest.cc",
"page_metrics_unittest.cc",
"uptime_monitor_unittest.cc",
"uptime_monitor_impl_unittest.cc",
]

deps = [
3 changes: 2 additions & 1 deletion browser/misc_metrics/misc_android_metrics.cc
Original file line number Diff line number Diff line change
@@ -4,9 +4,10 @@
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/browser/misc_metrics/misc_android_metrics.h"

#include "base/metrics/histogram_macros.h"
#include "brave/browser/misc_metrics/process_misc_metrics.h"
#include "brave/browser/misc_metrics/uptime_monitor.h"
#include "brave/browser/misc_metrics/uptime_monitor_impl.h"
#include "brave/browser/search_engines/search_engine_tracker.h"
#include "brave/components/misc_metrics/privacy_hub_metrics.h"
#include "brave/components/misc_metrics/tab_metrics.h"
9 changes: 5 additions & 4 deletions browser/misc_metrics/process_misc_metrics.cc
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
#include "brave/browser/misc_metrics/process_misc_metrics.h"

#include "brave/browser/misc_metrics/doh_metrics.h"
#include "brave/browser/misc_metrics/uptime_monitor.h"
#include "brave/browser/misc_metrics/uptime_monitor_impl.h"
#include "brave/components/ai_chat/core/browser/ai_chat_metrics.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
@@ -35,7 +35,8 @@ ProcessMiscMetrics::ProcessMiscMetrics(PrefService* local_state) {
#endif
ai_chat_metrics_ = std::make_unique<ai_chat::AIChatMetrics>(local_state);
doh_metrics_ = std::make_unique<misc_metrics::DohMetrics>(local_state);
uptime_monitor_ = std::make_unique<misc_metrics::UptimeMonitor>(local_state);
uptime_monitor_ =
std::make_unique<misc_metrics::UptimeMonitorImpl>(local_state);
}

ProcessMiscMetrics::~ProcessMiscMetrics() = default;
@@ -66,7 +67,7 @@ TabMetrics* ProcessMiscMetrics::tab_metrics() {
}
#endif

UptimeMonitor* ProcessMiscMetrics::uptime_monitor() {
UptimeMonitorImpl* ProcessMiscMetrics::uptime_monitor() {
return uptime_monitor_.get();
}

@@ -86,7 +87,7 @@ void ProcessMiscMetrics::RegisterPrefs(PrefRegistrySimple* registry) {
#endif
ai_chat::AIChatMetrics::RegisterPrefs(registry);
DohMetrics::RegisterPrefs(registry);
UptimeMonitor::RegisterPrefs(registry);
UptimeMonitorImpl::RegisterPrefs(registry);
}

} // namespace misc_metrics
6 changes: 3 additions & 3 deletions browser/misc_metrics/process_misc_metrics.h
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ class PrivacyHubMetrics;
class TabMetrics;
#endif
class DohMetrics;
class UptimeMonitor;
class UptimeMonitorImpl;

class ProcessMiscMetrics {
public:
@@ -50,7 +50,7 @@ class ProcessMiscMetrics {
PrivacyHubMetrics* privacy_hub_metrics();
TabMetrics* tab_metrics();
#endif
UptimeMonitor* uptime_monitor();
UptimeMonitorImpl* uptime_monitor();
ai_chat::AIChatMetrics* ai_chat_metrics();

private:
@@ -65,7 +65,7 @@ class ProcessMiscMetrics {
#endif
std::unique_ptr<ai_chat::AIChatMetrics> ai_chat_metrics_;
std::unique_ptr<DohMetrics> doh_metrics_;
std::unique_ptr<UptimeMonitor> uptime_monitor_;
std::unique_ptr<UptimeMonitorImpl> uptime_monitor_;
};

} // namespace misc_metrics
2 changes: 1 addition & 1 deletion browser/misc_metrics/sources.gni
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
brave_browser_misc_metrics_sources = [
"//brave/browser/misc_metrics/profile_misc_metrics_service.cc",
"//brave/browser/misc_metrics/profile_misc_metrics_service_factory.cc",
"//brave/browser/misc_metrics/uptime_monitor.cc",
"//brave/browser/misc_metrics/uptime_monitor_impl.cc",
]

brave_browser_misc_metrics_deps = [
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/browser/misc_metrics/uptime_monitor.h"
#include "brave/browser/misc_metrics/uptime_monitor_impl.h"

#include "base/time/time.h"
#include "brave/components/misc_metrics/pref_names.h"
#include "brave/components/p3a_utils/bucket.h"
@@ -27,14 +28,15 @@ constexpr int kBrowserOpenTimeBuckets[] = {30, 60, 120, 180, 300, 420, 600};

} // namespace

UptimeMonitor::UptimeMonitor(PrefService* local_state)
UptimeMonitorImpl::UptimeMonitorImpl(PrefService* local_state)
: local_state_(local_state),
report_frame_start_time_(
local_state->GetTime(kDailyUptimeFrameStartTimePrefName)),
report_frame_time_sum_(
local_state_->GetTimeDelta(kDailyUptimeSumPrefName)) {}
local_state_->GetTimeDelta(kDailyUptimeSumPrefName)),
weekly_storage_(local_state, kWeeklyUptimeStoragePrefName) {}

void UptimeMonitor::Init() {
void UptimeMonitorImpl::Init() {
if (report_frame_start_time_.is_null()) {
// If today is the first time monitoring uptime, set the frame start time
// to now.
@@ -43,33 +45,37 @@ void UptimeMonitor::Init() {
RecordP3A();
#if !BUILDFLAG(IS_ANDROID)
usage_clock_ = std::make_unique<UsageClock>();
timer_.Start(
FROM_HERE, kUsageTimeQueryInterval,
base::BindRepeating(&UptimeMonitor::RecordUsage, base::Unretained(this)));
timer_.Start(FROM_HERE, kUsageTimeQueryInterval,
base::BindRepeating(&UptimeMonitorImpl::RecordUsage,
base::Unretained(this)));
#endif
}

#if BUILDFLAG(IS_ANDROID)
void UptimeMonitor::ReportUsageDuration(base::TimeDelta duration) {
void UptimeMonitorImpl::ReportUsageDuration(base::TimeDelta duration) {
report_frame_time_sum_ += duration;
local_state_->SetTimeDelta(kDailyUptimeSumPrefName, report_frame_time_sum_);

weekly_storage_.AddDelta(duration.InSeconds());

RecordP3A();
}
#else
void UptimeMonitor::RecordUsage() {
void UptimeMonitorImpl::RecordUsage() {
const base::TimeDelta new_total = usage_clock_->GetTotalUsageTime();
const base::TimeDelta total_diff = new_total - current_total_usage_;
if (total_diff > base::TimeDelta()) {
report_frame_time_sum_ += total_diff;
current_total_usage_ = new_total;
local_state_->SetTimeDelta(kDailyUptimeSumPrefName, report_frame_time_sum_);

weekly_storage_.AddDelta(total_diff.InSeconds());
}
RecordP3A();
}
#endif

void UptimeMonitor::RecordP3A() {
void UptimeMonitorImpl::RecordP3A() {
if ((base::Time::Now() - report_frame_start_time_) <
kUsageTimeReportInterval) {
// Do not report, since 1 day has not passed.
@@ -81,27 +87,45 @@ void UptimeMonitor::RecordP3A() {
ResetReportFrame();
}

void UptimeMonitor::ResetReportFrame() {
void UptimeMonitorImpl::ResetReportFrame() {
report_frame_time_sum_ = base::TimeDelta();
report_frame_start_time_ = base::Time::Now();
local_state_->SetTimeDelta(kDailyUptimeSumPrefName, report_frame_time_sum_);
local_state_->SetTime(kDailyUptimeFrameStartTimePrefName,
report_frame_start_time_);
}

UptimeMonitor::~UptimeMonitor() = default;
base::TimeDelta UptimeMonitorImpl::GetUsedTimeInWeek() const {
return base::Seconds(weekly_storage_.GetWeeklySum());
}

base::WeakPtr<UptimeMonitor> UptimeMonitorImpl::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}

bool UptimeMonitorImpl::IsInUse() const {
#if !BUILDFLAG(IS_ANDROID)
return usage_clock_->IsInUse();
#else
return true;
#endif
}

UptimeMonitorImpl::~UptimeMonitorImpl() = default;

void UptimeMonitor::RegisterPrefs(PrefRegistrySimple* registry) {
void UptimeMonitorImpl::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterTimeDeltaPref(kDailyUptimeSumPrefName, base::TimeDelta());
registry->RegisterTimePref(kDailyUptimeFrameStartTimePrefName, base::Time());
registry->RegisterListPref(kWeeklyUptimeStoragePrefName);
}

void UptimeMonitor::RegisterPrefsForMigration(PrefRegistrySimple* registry) {
void UptimeMonitorImpl::RegisterPrefsForMigration(
PrefRegistrySimple* registry) {
// Added 10/2023
registry->RegisterListPref(kDailyUptimesListPrefName);
}

void UptimeMonitor::MigrateObsoletePrefs(PrefService* local_state) {
void UptimeMonitorImpl::MigrateObsoletePrefs(PrefService* local_state) {
// Added 10/2023
local_state->ClearPref(kDailyUptimesListPrefName);
}
Original file line number Diff line number Diff line change
@@ -3,13 +3,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_MISC_METRICS_UPTIME_MONITOR_H_
#define BRAVE_BROWSER_MISC_METRICS_UPTIME_MONITOR_H_
#ifndef BRAVE_BROWSER_MISC_METRICS_UPTIME_MONITOR_IMPL_H_
#define BRAVE_BROWSER_MISC_METRICS_UPTIME_MONITOR_IMPL_H_

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "brave/components/misc_metrics/uptime_monitor.h"
#include "brave/components/time_period_storage/weekly_storage.h"

class PrefService;
class PrefRegistrySimple;
@@ -23,12 +26,12 @@ class UsageClock;
inline constexpr char kBrowserOpenTimeHistogramName[] =
"Brave.Uptime.BrowserOpenTime.2";

class UptimeMonitor {
class UptimeMonitorImpl : public UptimeMonitor {
public:
explicit UptimeMonitor(PrefService* local_state);
UptimeMonitor(const UptimeMonitor&) = delete;
UptimeMonitor& operator=(const UptimeMonitor&) = delete;
~UptimeMonitor();
explicit UptimeMonitorImpl(PrefService* local_state);
UptimeMonitorImpl(const UptimeMonitorImpl&) = delete;
UptimeMonitorImpl& operator=(const UptimeMonitorImpl&) = delete;
~UptimeMonitorImpl() override;

static void RegisterPrefs(PrefRegistrySimple* registry);
static void RegisterPrefsForMigration(PrefRegistrySimple* registry);
@@ -40,6 +43,11 @@ class UptimeMonitor {
void ReportUsageDuration(base::TimeDelta duration);
#endif

// UptimeMonitor:
base::TimeDelta GetUsedTimeInWeek() const override;
base::WeakPtr<UptimeMonitor> GetWeakPtr() override;
bool IsInUse() const override;

private:
void RecordP3A();
#if !BUILDFLAG(IS_ANDROID)
@@ -60,8 +68,14 @@ class UptimeMonitor {

base::Time report_frame_start_time_;
base::TimeDelta report_frame_time_sum_;

// Weekly storage for uptime data
WeeklyStorage weekly_storage_;

// WeakPtr factory for this class
base::WeakPtrFactory<UptimeMonitor> weak_ptr_factory_{this};
};

} // namespace misc_metrics

#endif // BRAVE_BROWSER_MISC_METRICS_UPTIME_MONITOR_H_
#endif // BRAVE_BROWSER_MISC_METRICS_UPTIME_MONITOR_IMPL_H_
Original file line number Diff line number Diff line change
@@ -3,41 +3,42 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/browser/misc_metrics/uptime_monitor_impl.h"

#include <memory>

#include "base/test/metrics/histogram_tester.h"
#include "brave/browser/misc_metrics/uptime_monitor.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace misc_metrics {

class UptimeMonitorUnitTest : public testing::Test {
class UptimeMonitorImplUnitTest : public testing::Test {
public:
UptimeMonitorUnitTest()
UptimeMonitorImplUnitTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}

void SetUp() override {
misc_metrics::UptimeMonitor::RegisterPrefs(local_state_.registry());
misc_metrics::UptimeMonitorImpl::RegisterPrefs(local_state_.registry());

ResetMonitor();
}

protected:
void ResetMonitor() {
usage_monitor_ = std::make_unique<UptimeMonitor>(&local_state_);
usage_monitor_ = std::make_unique<UptimeMonitorImpl>(&local_state_);
usage_monitor_->Init();
}

content::BrowserTaskEnvironment task_environment_;
TestingPrefServiceSimple local_state_;
base::HistogramTester histogram_tester_;
std::unique_ptr<UptimeMonitor> usage_monitor_;
std::unique_ptr<UptimeMonitorImpl> usage_monitor_;
};

#if BUILDFLAG(IS_ANDROID)
TEST_F(UptimeMonitorUnitTest, ReportUsageDuration) {
TEST_F(UptimeMonitorImplUnitTest, ReportUsageDuration) {
histogram_tester_.ExpectTotalCount(kBrowserOpenTimeHistogramName, 0);

usage_monitor_->ReportUsageDuration(base::Minutes(15));
4 changes: 2 additions & 2 deletions chromium_src/chrome/browser/prefs/browser_prefs.cc
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
#include "brave/browser/brave_profile_prefs.h"
#include "brave/browser/brave_rewards/rewards_prefs_util.h"
#include "brave/browser/brave_stats/brave_stats_updater.h"
#include "brave/browser/misc_metrics/uptime_monitor.h"
#include "brave/browser/misc_metrics/uptime_monitor_impl.h"
#include "brave/browser/themes/brave_dark_mode_utils.h"
#include "brave/browser/translate/brave_translate_prefs_migration.h"
#include "brave/components/ai_chat/core/browser/model_service.h"
@@ -219,7 +219,7 @@ void MigrateObsoleteLocalStatePrefs(PrefService* local_state) {
brave_vpn::MigrateLocalStatePrefs(local_state);
#endif

misc_metrics::UptimeMonitor::MigrateObsoletePrefs(local_state);
misc_metrics::UptimeMonitorImpl::MigrateObsoletePrefs(local_state);
brave_search_conversion::p3a::MigrateObsoleteLocalStatePrefs(local_state);
brave_stats::MigrateObsoleteLocalStatePrefs(local_state);
p3a::StarRandomnessMeta::MigrateObsoleteLocalStatePrefs(local_state);
1 change: 1 addition & 0 deletions components/brave_vpn/browser/BUILD.gn
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ static_library("browser") {
"//brave/components/brave_vpn/common",
"//brave/components/brave_vpn/common/mojom",
"//brave/components/constants",
"//brave/components/misc_metrics",
"//brave/components/p3a_utils",
"//brave/components/resources:strings",
"//brave/components/skus/browser",
57 changes: 54 additions & 3 deletions components/brave_vpn/browser/brave_vpn_metrics.cc
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@

#include "brave/components/brave_vpn/browser/brave_vpn_metrics.h"

#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "brave/components/brave_vpn/common/brave_vpn_constants.h"
#include "brave/components/brave_vpn/common/pref_names.h"
@@ -19,21 +20,36 @@ namespace brave_vpn {
namespace {

constexpr int kWidgetUsageBuckets[] = {1, 10, 20};
// Buckets are tenths of a percent: 0%, 0.5%, 5%, 33%
constexpr int kVPNConnectedPercentageBuckets[] = {0, 5, 50, 330};
constexpr base::TimeDelta kConnectionReportInterval = base::Minutes(1);

} // namespace

BraveVpnMetrics::BraveVpnMetrics(PrefService* local_state,
PrefService* profile_prefs)
BraveVpnMetrics::BraveVpnMetrics(
PrefService* local_state,
PrefService* profile_prefs,
base::WeakPtr<misc_metrics::UptimeMonitor> uptime_monitor,
Delegate* delegate)
: local_state_(local_state),
profile_prefs_(profile_prefs),
uptime_monitor_(uptime_monitor),
delegate_(delegate),
widget_usage_storage_(local_state_,
prefs::kBraveVPNWidgetUsageWeeklyStorage) {
prefs::kBraveVPNWidgetUsageWeeklyStorage),
connected_minutes_storage_(
local_state_,
prefs::kBraveVPNConnectedMinutesWeeklyStorage) {
pref_change_registrar_.Init(profile_prefs);
pref_change_registrar_.Add(
kNewTabPageShowBraveVPN,
base::BindRepeating(&BraveVpnMetrics::HandleShowWidgetChange,
base::Unretained(this)));
RecordAllMetrics(false);

#if !BUILDFLAG(IS_ANDROID)
ReportVPNConnectedDuration();
#endif
}

BraveVpnMetrics::~BraveVpnMetrics() = default;
@@ -112,4 +128,39 @@ void BraveVpnMetrics::HandleShowWidgetChange() {
UMA_HISTOGRAM_BOOLEAN(kHideWidgetHistogramName, true);
}

void BraveVpnMetrics::RecordVPNConnectedInterval() {
connected_minutes_storage_.AddDelta(kConnectionReportInterval.InMinutes());
}

void BraveVpnMetrics::ReportVPNConnectedDuration() {
connection_report_timer_.Start(
FROM_HERE, base::Time::Now() + kConnectionReportInterval,
base::BindOnce(&BraveVpnMetrics::ReportVPNConnectedDuration,
base::Unretained(this)));

if (!delegate_->is_purchased_user() || !uptime_monitor_) {
return;
}

#if !BUILDFLAG(IS_ANDROID)
if (delegate_->IsConnected() && uptime_monitor_->IsInUse()) {
RecordVPNConnectedInterval();
}
#endif

auto total_browser_minutes = uptime_monitor_->GetUsedTimeInWeek().InMinutes();

auto connected_minutes = connected_minutes_storage_.GetWeeklySum();

int percentage = 0;
if (total_browser_minutes > 0) {
percentage =
static_cast<int>((connected_minutes * 1000) / total_browser_minutes);

p3a_utils::RecordToHistogramBucket(kVPNConnectedDurationHistogramName,
kVPNConnectedPercentageBuckets,
percentage);
}
}

} // namespace brave_vpn
31 changes: 30 additions & 1 deletion components/brave_vpn/browser/brave_vpn_metrics.h
Original file line number Diff line number Diff line change
@@ -6,8 +6,11 @@
#ifndef BRAVE_COMPONENTS_BRAVE_VPN_BROWSER_BRAVE_VPN_METRICS_H_
#define BRAVE_COMPONENTS_BRAVE_VPN_BROWSER_BRAVE_VPN_METRICS_H_

#include "base/memory/weak_ptr.h"
#include "base/timer/wall_clock_timer.h"
#include "brave/components/misc_metrics/uptime_monitor.h"
#include "brave/components/time_period_storage/weekly_storage.h"
#include "build/build_config.h"
#include "components/prefs/pref_change_registrar.h"

class PrefService;
@@ -21,10 +24,26 @@ inline constexpr char kDaysInMonthUsedHistogramName[] =
inline constexpr char kLastUsageTimeHistogramName[] = "Brave.VPN.LastUsageTime";
inline constexpr char kWidgetUsageHistogramName[] = "Brave.VPN.WidgetUsage";
inline constexpr char kHideWidgetHistogramName[] = "Brave.VPN.HideWidget";
inline constexpr char kVPNConnectedDurationHistogramName[] =
"Brave.VPN.ConnectedDuration";

class BraveVpnMetrics {
public:
BraveVpnMetrics(PrefService* local_prefs, PrefService* profile_prefs);
class Delegate {
public:
virtual ~Delegate() = default;

virtual bool is_purchased_user() const = 0;

#if !BUILDFLAG(IS_ANDROID)
virtual bool IsConnected() const = 0;
#endif
};

BraveVpnMetrics(PrefService* local_prefs,
PrefService* profile_prefs,
base::WeakPtr<misc_metrics::UptimeMonitor> uptime_monitor,
Delegate* delegate = nullptr);
~BraveVpnMetrics();

BraveVpnMetrics(const BraveVpnMetrics&) = delete;
@@ -41,15 +60,25 @@ class BraveVpnMetrics {

void RecordWidgetUsage(bool new_usage);

// Records one minute of VPN connected time
void RecordVPNConnectedInterval();

private:
void HandleShowWidgetChange();

void ReportVPNConnectedDuration();

raw_ptr<PrefService> local_state_;
raw_ptr<PrefService> profile_prefs_;
PrefChangeRegistrar pref_change_registrar_;
base::WeakPtr<misc_metrics::UptimeMonitor> uptime_monitor_;
raw_ptr<Delegate> delegate_ = nullptr;

WeeklyStorage widget_usage_storage_;
WeeklyStorage connected_minutes_storage_;
base::WallClockTimer report_timer_;

base::WallClockTimer connection_report_timer_;
};

} // namespace brave_vpn
76 changes: 74 additions & 2 deletions components/brave_vpn/browser/brave_vpn_metrics_unittest.cc
Original file line number Diff line number Diff line change
@@ -19,6 +19,44 @@ namespace brave_vpn {

class BraveVpnMetricsTest : public testing::Test {
public:
// Mock implementation of UptimeMonitor for testing
class MockUptimeMonitor : public misc_metrics::UptimeMonitor {
public:
MockUptimeMonitor() = default;
~MockUptimeMonitor() override = default;

// UptimeMonitor implementation
base::TimeDelta GetUsedTimeInWeek() const override { return usage_time_; }
base::WeakPtr<misc_metrics::UptimeMonitor> GetWeakPtr() override {
return weak_ptr_factory_.GetWeakPtr();
}
bool IsInUse() const override { return is_in_use; }

base::TimeDelta usage_time_;
bool is_in_use = true;

private:
base::WeakPtrFactory<MockUptimeMonitor> weak_ptr_factory_{this};
};

// Mock implementation of BraveVpnMetrics::Delegate for testing
class MockVpnDelegate : public BraveVpnMetrics::Delegate {
public:
MockVpnDelegate() = default;
~MockVpnDelegate() override = default;

// BraveVpnMetrics::Delegate implementation
bool is_purchased_user() const override { return is_purchased; }
#if !BUILDFLAG(IS_ANDROID)
bool IsConnected() const override { return is_connected; }
#endif

bool is_purchased = true;
#if !BUILDFLAG(IS_ANDROID)
bool is_connected = false;
#endif
};

BraveVpnMetricsTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}

@@ -30,14 +68,17 @@ class BraveVpnMetricsTest : public testing::Test {
profile_prefs_.registry()->RegisterBooleanPref(kNewTabPageShowBraveVPN,
true);
brave_vpn::RegisterLocalStatePrefs(local_state_.registry());
metrics_ =
std::make_unique<BraveVpnMetrics>(&local_state_, &profile_prefs_);

metrics_ = std::make_unique<BraveVpnMetrics>(
&local_state_, &profile_prefs_, uptime_monitor.GetWeakPtr(), &delegate);
}

protected:
content::BrowserTaskEnvironment task_environment_;
TestingPrefServiceSimple local_state_;
sync_preferences::TestingPrefServiceSyncable profile_prefs_;
MockUptimeMonitor uptime_monitor;
MockVpnDelegate delegate;
std::unique_ptr<BraveVpnMetrics> metrics_;
base::HistogramTester histogram_tester_;
};
@@ -112,4 +153,35 @@ TEST_F(BraveVpnMetricsTest, WidgetUsageAndHideMetrics) {
histogram_tester_.ExpectTotalCount(kWidgetUsageHistogramName, 3);
}

#if !BUILDFLAG(IS_ANDROID)
TEST_F(BraveVpnMetricsTest, ConnectedDuration) {
uptime_monitor.usage_time_ = base::Minutes(100);
uptime_monitor.is_in_use = true;
delegate.is_purchased = false;
delegate.is_connected = false;

histogram_tester_.ExpectTotalCount(kVPNConnectedDurationHistogramName, 0);

task_environment_.FastForwardBy(base::Minutes(5));
histogram_tester_.ExpectTotalCount(kVPNConnectedDurationHistogramName, 0);

delegate.is_purchased = true;

task_environment_.FastForwardBy(base::Minutes(5));
histogram_tester_.ExpectUniqueSample(kVPNConnectedDurationHistogramName, 0,
5);

delegate.is_connected = true;

task_environment_.FastForwardBy(base::Minutes(4));
histogram_tester_.ExpectBucketCount(kVPNConnectedDurationHistogramName, 2, 4);

delegate.is_connected = false;

task_environment_.FastForwardBy(base::Minutes(30));
histogram_tester_.ExpectBucketCount(kVPNConnectedDurationHistogramName, 2,
34);
}
#endif

} // namespace brave_vpn
7 changes: 6 additions & 1 deletion components/brave_vpn/browser/brave_vpn_service.cc
Original file line number Diff line number Diff line change
@@ -46,13 +46,14 @@ BraveVpnService::BraveVpnService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* local_prefs,
PrefService* profile_prefs,
base::WeakPtr<misc_metrics::UptimeMonitor> uptime_monitor,
base::RepeatingCallback<mojo::PendingRemote<skus::mojom::SkusService>()>
skus_service_getter)
: local_prefs_(local_prefs),
profile_prefs_(profile_prefs),
skus_service_getter_(skus_service_getter),
api_request_(new BraveVpnAPIRequest(url_loader_factory)),
brave_vpn_metrics_(local_prefs, profile_prefs) {
brave_vpn_metrics_(local_prefs, profile_prefs, uptime_monitor, this) {
DCHECK(IsBraveVPNFeatureEnabled());
#if !BUILDFLAG(IS_ANDROID)
DCHECK(connection_manager);
@@ -125,6 +126,10 @@ std::string BraveVpnService::GetCurrentEnvironment() const {
return local_prefs_->GetString(prefs::kBraveVPNEnvironment);
}

bool BraveVpnService::is_purchased_user() const {
return GetPurchasedInfoSync().state == mojom::PurchasedState::PURCHASED;
}

void BraveVpnService::ReloadPurchasedState() {
LoadPurchasedState(skus::GetDomain("vpn", GetCurrentEnvironment()));
}
15 changes: 10 additions & 5 deletions components/brave_vpn/browser/brave_vpn_service.h
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/sequence_checker.h"
#include "base/timer/timer.h"
@@ -40,6 +41,10 @@ namespace network {
class SharedURLLoaderFactory;
} // namespace network

namespace misc_metrics {
class UptimeMonitor;
} // namespace misc_metrics

class PrefService;

#if !BUILDFLAG(IS_ANDROID)
@@ -60,13 +65,15 @@ class BraveVpnService :
public BraveVPNConnectionManager::Observer,
#endif
public mojom::ServiceHandler,
public KeyedService {
public KeyedService,
public BraveVpnMetrics::Delegate {
public:
BraveVpnService(
BraveVPNConnectionManager* connection_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* local_prefs,
PrefService* profile_prefs,
base::WeakPtr<misc_metrics::UptimeMonitor> uptime_monitor,
base::RepeatingCallback<mojo::PendingRemote<skus::mojom::SkusService>()>
skus_service_getter);
~BraveVpnService() override;
@@ -79,16 +86,14 @@ class BraveVpnService :
#endif // BUILDFLAG(IS_ANDROID)

std::string GetCurrentEnvironment() const;
bool is_purchased_user() const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to leave is_purchased_user() and IsConnected() as BraveVpnService's own api because both are part of vpn service's functionality.
IMO, we can simply call it via delegate's interface (ex, Delegate::GetIsPurchasedUser() or Delegate::GetIsConnected()) w/o name collision. WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i did something very similar for Leo, where I included existing methods in the delegate:

class ConversationHandlerForMetrics {
public:
virtual ~ConversationHandlerForMetrics() = default;
virtual size_t GetConversationHistorySize() = 0;
virtual bool should_send_page_contents() const = 0;
virtual mojom::APIError current_error() const = 0;
};

imho, it seems odd to have duplicated methods (i.e. having IsConnected and GetIsConnected side-by-side), especially for these getters.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not block merging with this but still it seems strange to me to get vpn service's core state from metrics interface.

return GetPurchasedInfoSync().state == mojom::PurchasedState::PURCHASED;
}
bool is_purchased_user() const override;
void BindInterface(mojo::PendingReceiver<mojom::ServiceHandler> receiver);
void ReloadPurchasedState();
bool IsBraveVPNEnabled() const;
#if !BUILDFLAG(IS_ANDROID)
void ToggleConnection();
mojom::ConnectionState GetConnectionState() const;
bool IsConnected() const;
bool IsConnected() const override;

// mojom::vpn::ServiceHandler
void GetConnectionState(GetConnectionStateCallback callback) override;
2 changes: 1 addition & 1 deletion components/brave_vpn/browser/brave_vpn_service_unittest.cc
Original file line number Diff line number Diff line change
@@ -253,7 +253,7 @@ class BraveVPNServiceTest : public testing::Test {
nullptr,
#endif
url_loader_factory_.GetSafeWeakWrapper(), &local_pref_service_,
&profile_pref_service_,
&profile_pref_service_, nullptr,
base::BindRepeating(&BraveVPNServiceTest::GetSkusService,
base::Unretained(this)));
}
1 change: 1 addition & 0 deletions components/brave_vpn/common/brave_vpn_utils.cc
Original file line number Diff line number Diff line change
@@ -56,6 +56,7 @@ void RegisterVPNLocalStatePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kBraveVPNSmartProxyRoutingEnabled,
false);
registry->RegisterListPref(prefs::kBraveVPNWidgetUsageWeeklyStorage);
registry->RegisterListPref(prefs::kBraveVPNConnectedMinutesWeeklyStorage);
}

#if !BUILDFLAG(IS_ANDROID)
2 changes: 2 additions & 0 deletions components/brave_vpn/common/pref_names.h
Original file line number Diff line number Diff line change
@@ -90,6 +90,8 @@ inline constexpr char kBraveVPNDaysInMonthUsed[] =
"brave.brave_vpn.days_in_month_used";
inline constexpr char kBraveVPNWidgetUsageWeeklyStorage[] =
"brave.brave_vpn.widget_usage";
inline constexpr char kBraveVPNConnectedMinutesWeeklyStorage[] =
"brave.brave_vpn.connected_minutes";
} // namespace prefs

} // namespace brave_vpn
1 change: 1 addition & 0 deletions components/misc_metrics/BUILD.gn
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ static_library("misc_metrics") {
"split_view_metrics.h",
"tab_metrics.cc",
"tab_metrics.h",
"uptime_monitor.h",
]

deps = [
2 changes: 2 additions & 0 deletions components/misc_metrics/pref_names.h
Original file line number Diff line number Diff line change
@@ -52,6 +52,8 @@ inline constexpr char kDailyUptimeSumPrefName[] =
"brave.misc_metrics.uptime_sum";
inline constexpr char kDailyUptimeFrameStartTimePrefName[] =
"brave.misc_metrics.uptime_frame_start_time_v2";
inline constexpr char kWeeklyUptimeStoragePrefName[] =
"brave.misc_metrics.weekly_uptime_storage";

inline constexpr char kMiscMetricsTabSwitcherNewTabsStorage[] =
"brave.misc_metrics.tab_switcher_new_tabs_storage";
31 changes: 31 additions & 0 deletions components/misc_metrics/uptime_monitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_COMPONENTS_MISC_METRICS_UPTIME_MONITOR_H_
#define BRAVE_COMPONENTS_MISC_METRICS_UPTIME_MONITOR_H_

#include "base/memory/weak_ptr.h"
#include "base/time/time.h"

namespace misc_metrics {

// Interface for uptime monitoring functionality
class UptimeMonitor {
public:
virtual ~UptimeMonitor() = default;

// Returns the total browser usage time for the current week
virtual base::TimeDelta GetUsedTimeInWeek() const = 0;

// Returns a weak pointer to this instance
virtual base::WeakPtr<UptimeMonitor> GetWeakPtr() = 0;

// Returns true if browser is currently considered to be in use
virtual bool IsInUse() const = 0;
};

} // namespace misc_metrics

#endif // BRAVE_COMPONENTS_MISC_METRICS_UPTIME_MONITOR_H_
1 change: 1 addition & 0 deletions components/p3a/metric_names.h
Original file line number Diff line number Diff line change
@@ -196,6 +196,7 @@ inline constexpr auto kCollectedTypicalHistograms =
{"Brave.Today.WeeklySessionCount", {}},
{"Brave.Today.WeeklyTotalCardClicks", MetricConfig{.ephemeral = true}},
{"Brave.Today.WeeklyTotalCardViews", {}},
{"Brave.VPN.ConnectedDuration", MetricConfig{.ephemeral = true}},
{"Brave.VPN.HideWidget", MetricConfig{.ephemeral = true}},
{"Brave.VPN.LastUsageTime", MetricConfig{.record_activation_date = true}},
{"Brave.VPN.NewUserReturning", MetricConfig{