Added VPN usage P3A

This commit is contained in:
Darnell Andries
2022-08-09 18:29:43 -07:00
parent 407ad4dd4e
commit 0c58258a8a
22 changed files with 433 additions and 49 deletions
@@ -255,6 +255,8 @@ public abstract class BraveActivity<C extends ChromeActivityComponent> extends C
if (BraveVpnUtils.isBraveVpnFeatureEnable()) {
InAppPurchaseWrapper.getInstance().startBillingServiceConnection(BraveActivity.this);
BraveVpnNativeWorker.getInstance().addObserver(this);
BraveVpnUtils.reportBackgroundUsageP3A();
}
Tab tab = getActivityTab();
if (tab != null) {
@@ -172,6 +172,15 @@ public class BraveVpnNativeWorker {
mNativeBraveVpnNativeWorker, purchaseToken, productId, productType, packageName);
}
public void reportBackgroundP3A(long sessionStartTimeMs, long sessionEndTimeMs) {
BraveVpnNativeWorkerJni.get().reportBackgroundP3A(
mNativeBraveVpnNativeWorker, sessionStartTimeMs, sessionEndTimeMs);
}
public void reportForegroundP3A() {
BraveVpnNativeWorkerJni.get().reportForegroundP3A(mNativeBraveVpnNativeWorker);
}
@NativeMethods
interface Natives {
void init(BraveVpnNativeWorker caller);
@@ -190,5 +199,8 @@ public class BraveVpnNativeWorker {
String packageName);
void verifyPurchaseToken(long nativeBraveVpnNativeWorker, String purchaseToken,
String productId, String productType, String packageName);
void reportBackgroundP3A(
long nativeBraveVpnNativeWorker, long sessionStartTimeMs, long sessionEndTimeMs);
void reportForegroundP3A(long nativeBraveVpnNativeWorker);
}
}
@@ -47,6 +47,8 @@ public class BraveVpnPrefUtils {
public static final String PREF_BRAVE_VPN_SERVER_PUBLIC_KEY = "brave_vpn_server_public_key";
public static final String PREF_BRAVE_VPN_IP_ADDRESS = "brave_vpn_ip_address";
public static final String PREF_BRAVE_VPN_CLIENT_PRIVATE_KEY = "brave_vpn_client_private_key";
public static final String PREF_SESSION_START_TIME = "brave_vpn_session_start_time";
public static final String PREF_SESSION_END_TIME = "brave_vpn_session_end_time";
private static final SharedPreferences mSharedPreferences =
ContextUtils.getAppSharedPreferences();
@@ -268,4 +270,24 @@ public class BraveVpnPrefUtils {
sharedPreferencesEditor.putStringSet(PREF_EXCLUDED_PACKAGES, packages);
sharedPreferencesEditor.apply();
}
public static void setSessionEndTimeMs(long timeMs) {
SharedPreferences.Editor sharedPreferencesEditor = mSharedPreferences.edit();
sharedPreferencesEditor.putLong(PREF_SESSION_END_TIME, timeMs);
sharedPreferencesEditor.apply();
}
public static long getSessionEndTimeMs() {
return mSharedPreferences.getLong(PREF_SESSION_END_TIME, -1);
}
public static void setSessionStartTimeMs(long timeMs) {
SharedPreferences.Editor sharedPreferencesEditor = mSharedPreferences.edit();
sharedPreferencesEditor.putLong(PREF_SESSION_START_TIME, timeMs);
sharedPreferencesEditor.apply();
}
public static long getSessionStartTimeMs() {
return mSharedPreferences.getLong(PREF_SESSION_START_TIME, -1);
}
}
@@ -17,6 +17,7 @@ import android.os.Build;
import androidx.core.content.ContextCompat;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.vpn.BraveVpnNativeWorker;
import org.chromium.chrome.browser.vpn.wireguard.WireguardService;
import org.chromium.chrome.browser.vpn.wireguard.WireguardUtils;
import org.chromium.ui.widget.Toast;
@@ -62,6 +63,7 @@ public class BraveVpnProfileUtils {
}
public void startVpn(Context context) {
BraveVpnNativeWorker.getInstance().reportForegroundP3A();
ContextCompat.startForegroundService(context, new Intent(context, WireguardService.class));
}
@@ -25,6 +25,7 @@ import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.util.BraveConstants;
import org.chromium.chrome.browser.vpn.BraveVpnNativeWorker;
import org.chromium.chrome.browser.vpn.activities.BraveVpnPlansActivity;
import org.chromium.chrome.browser.vpn.activities.BraveVpnProfileActivity;
import org.chromium.chrome.browser.vpn.activities.BraveVpnSupportActivity;
@@ -241,4 +242,13 @@ public class BraveVpnUtils {
((FragmentActivity) activity).getSupportFragmentManager(),
"BraveVpnConfirmDialogFragment");
}
public static void reportBackgroundUsageP3A() {
// Will report previous/current session timestamps...
BraveVpnNativeWorker.getInstance().reportBackgroundP3A(
BraveVpnPrefUtils.getSessionStartTimeMs(), BraveVpnPrefUtils.getSessionEndTimeMs());
// ...and then reset the timestamps so we don't report the same session again.
BraveVpnPrefUtils.setSessionStartTimeMs(-1);
BraveVpnPrefUtils.setSessionEndTimeMs(-1);
}
}
@@ -34,6 +34,7 @@ import org.chromium.chrome.browser.vpn.DisconnectVpnBroadcastReceiver;
import org.chromium.chrome.browser.vpn.utils.BraveVpnPrefUtils;
import org.chromium.chrome.browser.vpn.utils.BraveVpnUtils;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
@@ -43,6 +44,7 @@ public class WireguardServiceImpl
private TunnelModel mTunnelModel;
private final IBinder mBinder = new LocalBinder();
private Timer mVpnStatisticsTimer;
private Timer mRecordDaysUsedTimer;
private static final int BRAVE_VPN_NOTIFICATION_ID = 801;
private Context mContext = ContextUtils.getApplicationContext();
@@ -89,6 +91,8 @@ public class WireguardServiceImpl
mTunnelModel = TunnelModel.createTunnel(config, this);
mBackend.setState(mTunnelModel, Tunnel.State.UP, config);
updateVpnStatisticsTimer();
recordSessionTimes();
updateRecordSessionTimesTimer();
}
private Notification getBraveVpnNotification(String notificationText) {
@@ -124,6 +128,39 @@ public class WireguardServiceImpl
mNotificationManager.notify(BRAVE_VPN_NOTIFICATION_ID, notification);
}
private void recordSessionTimes() {
long sessionStartTimeMs = BraveVpnPrefUtils.getSessionStartTimeMs();
long sessionEndTimeMs = BraveVpnPrefUtils.getSessionEndTimeMs();
Calendar sessionEndTimeCal = Calendar.getInstance();
sessionEndTimeCal.setTimeInMillis(sessionEndTimeMs);
Calendar currDate = Calendar.getInstance();
long currTimeMs = System.currentTimeMillis();
if (sessionStartTimeMs < 0) {
BraveVpnPrefUtils.setSessionStartTimeMs(currTimeMs);
}
if (currDate.get(Calendar.YEAR) != sessionEndTimeCal.get(Calendar.YEAR)
|| currDate.get(Calendar.MONTH) != sessionEndTimeCal.get(Calendar.MONTH)
|| currDate.get(Calendar.DAY_OF_MONTH)
!= sessionEndTimeCal.get(Calendar.DAY_OF_MONTH)) {
BraveVpnPrefUtils.setSessionEndTimeMs(currTimeMs);
}
}
private void updateRecordSessionTimesTimer() {
// This timer will be active for the duration of the connection.
// It will become inactive when the connection is terminated/service destroyed.
mVpnStatisticsTimer = new Timer();
mVpnStatisticsTimer.schedule(new TimerTask() {
@Override
public void run() {
recordSessionTimes();
}
}, 0, 60000);
}
private void updateVpnStatisticsTimer() {
mVpnStatisticsTimer = new Timer();
mVpnStatisticsTimer.schedule(new TimerTask() {
+9
View File
@@ -14,6 +14,7 @@
#include "brave/components/brave_search_conversion/p3a.h"
#include "brave/components/brave_shields/browser/ad_block_service.h"
#include "brave/components/brave_shields/browser/brave_shields_p3a.h"
#include "brave/components/brave_vpn/buildflags/buildflags.h"
#include "brave/components/constants/pref_names.h"
#include "brave/components/decentralized_dns/utils.h"
#include "brave/components/ntp_background_images/browser/ntp_background_images_service.h"
@@ -42,6 +43,10 @@
#include "chrome/browser/first_run/first_run.h"
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(ENABLE_BRAVE_VPN)
#include "brave/components/brave_vpn/pref_names.h"
#endif
#if BUILDFLAG(ENABLE_WIDEVINE)
#include "brave/browser/widevine/widevine_utils.h"
#endif
@@ -110,6 +115,10 @@ void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
RegisterLocalStatePrefsForMigration(registry);
brave_search_conversion::p3a::RegisterLocalStatePrefs(registry);
#if BUILDFLAG(ENABLE_BRAVE_VPN)
brave_vpn::prefs::RegisterLocalStatePrefs(registry);
#endif
}
} // namespace brave
@@ -245,6 +245,24 @@ void BraveVpnNativeWorker::OnVerifyPurchaseToken(
base::android::ConvertUTF8ToJavaString(env, json_response), success);
}
void BraveVpnNativeWorker::ReportForegroundP3A(JNIEnv* env) {
BraveVpnService* brave_vpn_service = GetBraveVpnService();
if (brave_vpn_service) {
// Reporting a new session to P3A functions.
brave_vpn_service->RecordP3A(true);
}
}
void BraveVpnNativeWorker::ReportBackgroundP3A(JNIEnv* env,
jlong session_start_time_ms,
jlong session_end_time_ms) {
BraveVpnService* brave_vpn_service = GetBraveVpnService();
if (brave_vpn_service) {
brave_vpn_service->RecordAndroidBackgroundP3A(session_start_time_ms,
session_end_time_ms);
}
}
static void JNI_BraveVpnNativeWorker_Init(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
@@ -90,6 +90,11 @@ class BraveVpnNativeWorker {
void OnVerifyPurchaseToken(const std::string& json_response, bool success);
void ReportForegroundP3A(JNIEnv* env);
void ReportBackgroundP3A(JNIEnv* env,
jlong session_start_time_ms,
jlong session_end_time_ms);
private:
JavaObjectWeakGlobalRef weak_java_brave_vpn_native_worker_;
base::WeakPtrFactory<BraveVpnNativeWorker> weak_factory_;
@@ -13,6 +13,7 @@
#include "brave/components/brave_vpn/brave_vpn_utils.h"
#include "brave/components/skus/common/features.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -69,14 +70,16 @@ KeyedService* BraveVpnServiceFactory::BuildServiceInstanceFor(
auto* default_storage_partition = context->GetDefaultStoragePartition();
auto shared_url_loader_factory =
default_storage_partition->GetURLLoaderFactoryForBrowserProcess();
auto* local_state = g_browser_process->local_state();
auto callback = base::BindRepeating(
[](content::BrowserContext* context) {
return skus::SkusServiceFactory::GetForContext(context);
},
context);
auto* vpn_service = new BraveVpnService(
shared_url_loader_factory, user_prefs::UserPrefs::Get(context), callback);
auto* vpn_service =
new BraveVpnService(shared_url_loader_factory, local_state,
user_prefs::UserPrefs::Get(context), callback);
#if BUILDFLAG(IS_WIN)
auto* dns_observer_service =
brave_vpn::BraveVpnDnsObserverFactory::GetInstance()
+2
View File
@@ -26,6 +26,7 @@ static_library("brave_vpn") {
"mojom",
"//base",
"//brave/components/api_request_helper:api_request_helper",
"//brave/components/p3a_utils",
"//brave/components/resources:strings",
"//brave/components/skus/browser",
"//brave/components/skus/common",
@@ -113,6 +114,7 @@ source_set("unit_tests") {
deps = [
":brave_vpn",
"//base",
"//brave/components/p3a_utils",
"//brave/components/skus/browser",
"//brave/components/skus/common",
"//brave/components/skus/common:mojom",
+87 -15
View File
@@ -12,8 +12,10 @@
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "brave/components/brave_vpn/brave_vpn_utils.h"
#include "brave/components/brave_vpn/pref_names.h"
#include "brave/components/p3a_utils/feature_usage.h"
#include "brave/components/skus/browser/skus_utils.h"
#include "components/prefs/pref_service.h"
#include "net/base/network_change_notifier.h"
@@ -53,6 +55,8 @@ constexpr char kVerifyPurchaseToken[] = "api/v1.1/verify-purchase-token";
constexpr char kCreateSubscriberCredentialV12[] =
"api/v1.2/subscriber-credential/create";
constexpr int kP3AIntervalHours = 24;
net::NetworkTrafficAnnotationTag GetNetworkTrafficAnnotationTag() {
return net::DefineNetworkTrafficAnnotation("brave_vpn_service", R"(
semantics {
@@ -114,10 +118,12 @@ using PurchasedState = mojom::PurchasedState;
BraveVpnService::BraveVpnService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* prefs,
PrefService* local_prefs,
PrefService* profile_prefs,
base::RepeatingCallback<mojo::PendingRemote<skus::mojom::SkusService>()>
skus_service_getter)
: prefs_(prefs),
: local_prefs_(local_prefs),
profile_prefs_(profile_prefs),
skus_service_getter_(skus_service_getter),
api_request_helper_(GetNetworkTrafficAnnotationTag(),
url_loader_factory) {
@@ -137,18 +143,21 @@ BraveVpnService::BraveVpnService(
// To prevent this, we load purchased state at startup only
// when profile has cached region list because region list is fetched
// and cached only when user purchased at least once.
auto* preference = prefs_->FindPreference(prefs::kBraveVPNRegionList);
auto* preference = profile_prefs_->FindPreference(prefs::kBraveVPNRegionList);
if (preference && !preference->IsDefaultValue()) {
ReloadPurchasedState();
}
base::PowerMonitor::AddPowerSuspendObserver(this);
#endif // !BUILDFLAG(IS_ANDROID)
InitP3A();
}
BraveVpnService::~BraveVpnService() {}
std::string BraveVpnService::GetCurrentEnvironment() const {
return prefs_->GetString(prefs::kBraveVPNEEnvironment);
return profile_prefs_->GetString(prefs::kBraveVPNEEnvironment);
}
void BraveVpnService::ReloadPurchasedState() {
@@ -265,6 +274,8 @@ void BraveVpnService::OnConnected() {
}
UpdateAndNotifyConnectionStateChange(ConnectionState::CONNECTED);
RecordP3A(true);
}
void BraveVpnService::OnIsConnecting() {
@@ -445,7 +456,7 @@ void BraveVpnService::LoadCachedRegionData() {
if (GetDeviceRegion().empty())
return;
auto* preference = prefs_->FindPreference(prefs::kBraveVPNRegionList);
auto* preference = profile_prefs_->FindPreference(prefs::kBraveVPNRegionList);
DCHECK(preference);
// Early return when we don't have any cached region data.
if (preference->IsDefaultValue())
@@ -472,7 +483,8 @@ void BraveVpnService::SetRegionListToPrefs() {
for (const auto& region : regions_) {
regions_list.Append(GetValueFromRegion(region));
}
prefs_->Set(prefs::kBraveVPNRegionList, base::Value(std::move(regions_list)));
profile_prefs_->Set(prefs::kBraveVPNRegionList,
base::Value(std::move(regions_list)));
}
void BraveVpnService::OnFetchRegionList(bool background_fetch,
@@ -582,22 +594,22 @@ void BraveVpnService::SetDeviceRegionWithTimezone(
void BraveVpnService::SetDeviceRegion(const std::string& name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
prefs_->SetString(prefs::kBraveVPNDeviceRegion, name);
profile_prefs_->SetString(prefs::kBraveVPNDeviceRegion, name);
}
void BraveVpnService::SetSelectedRegion(const std::string& name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
prefs_->SetString(prefs::kBraveVPNSelectedRegion, name);
profile_prefs_->SetString(prefs::kBraveVPNSelectedRegion, name);
}
std::string BraveVpnService::GetDeviceRegion() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return prefs_->GetString(prefs::kBraveVPNDeviceRegion);
return profile_prefs_->GetString(prefs::kBraveVPNDeviceRegion);
}
std::string BraveVpnService::GetSelectedRegion() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return prefs_->GetString(prefs::kBraveVPNSelectedRegion);
return profile_prefs_->GetString(prefs::kBraveVPNSelectedRegion);
}
void BraveVpnService::SetFallbackDeviceRegion() {
@@ -882,17 +894,18 @@ void BraveVpnService::GetPurchaseToken(GetPurchaseTokenCallback callback) {
// Get the Android purchase token (for Google Play Store).
// The value for this is validated on the account.brave.com side
auto* purchase_token =
prefs_->FindPreference(prefs::kBraveVPNPurchaseTokenAndroid);
profile_prefs_->FindPreference(prefs::kBraveVPNPurchaseTokenAndroid);
if (purchase_token && !purchase_token->IsDefaultValue()) {
purchase_token_string =
prefs_->GetString(prefs::kBraveVPNPurchaseTokenAndroid);
profile_prefs_->GetString(prefs::kBraveVPNPurchaseTokenAndroid);
}
// Package name is important; for real users, it'll be the Release package.
// For testing we do have the ability to use the Nightly package.
auto* package = prefs_->FindPreference(prefs::kBraveVPNPackageAndroid);
auto* package =
profile_prefs_->FindPreference(prefs::kBraveVPNPackageAndroid);
if (package && !package->IsDefaultValue()) {
package_string = prefs_->GetString(prefs::kBraveVPNPackageAndroid);
package_string = profile_prefs_->GetString(prefs::kBraveVPNPackageAndroid);
}
base::Value response(base::Value::Type::DICTIONARY);
@@ -1065,6 +1078,65 @@ void BraveVpnService::OnPrepareCredentialsPresentation(
#endif
}
void BraveVpnService::InitP3A() {
p3a_timer_.Start(FROM_HERE, base::Hours(kP3AIntervalHours), this,
&BraveVpnService::OnP3AInterval);
RecordP3A(false);
}
void BraveVpnService::RecordP3A(bool new_usage) {
if (new_usage) {
p3a_utils::RecordFeatureUsage(local_prefs_, prefs::kBraveVPNFirstUseTime,
prefs::kBraveVPNLastUseTime);
}
p3a_utils::RecordFeatureNewUserReturning(
local_prefs_, prefs::kBraveVPNFirstUseTime, prefs::kBraveVPNLastUseTime,
prefs::kBraveVPNUsedSecondDay, kNewUserReturningHistogramName);
p3a_utils::RecordFeatureDaysInMonthUsed(
local_prefs_, new_usage, prefs::kBraveVPNLastUseTime,
prefs::kBraveVPNDaysInMonthUsed, kDaysInMonthUsedHistogramName);
p3a_utils::RecordFeatureLastUsageTimeMetric(
local_prefs_, prefs::kBraveVPNLastUseTime, kLastUsageTimeHistogramName);
}
#if BUILDFLAG(IS_ANDROID)
void BraveVpnService::RecordAndroidBackgroundP3A(int64_t session_start_time_ms,
int64_t session_end_time_ms) {
if (session_start_time_ms < 0 || session_end_time_ms < 0) {
RecordP3A(false);
return;
}
base::Time session_start_time =
base::Time::FromJsTime(static_cast<double>(session_start_time_ms))
.LocalMidnight();
base::Time session_end_time =
base::Time::FromJsTime(static_cast<double>(session_end_time_ms))
.LocalMidnight();
for (base::Time day = session_start_time; day <= session_end_time;
day += base::Days(1)) {
bool is_last_day = day == session_end_time;
// Call functions for each day in the last session to ensure
// p3a_util functions produce the correct result
p3a_utils::RecordFeatureUsage(local_prefs_, prefs::kBraveVPNFirstUseTime,
prefs::kBraveVPNLastUseTime, day);
p3a_utils::RecordFeatureNewUserReturning(
local_prefs_, prefs::kBraveVPNFirstUseTime, prefs::kBraveVPNLastUseTime,
prefs::kBraveVPNUsedSecondDay, kNewUserReturningHistogramName,
is_last_day);
p3a_utils::RecordFeatureDaysInMonthUsed(
local_prefs_, day, prefs::kBraveVPNLastUseTime,
prefs::kBraveVPNDaysInMonthUsed, kDaysInMonthUsedHistogramName,
is_last_day);
}
p3a_utils::RecordFeatureLastUsageTimeMetric(
local_prefs_, prefs::kBraveVPNLastUseTime, kLastUsageTimeHistogramName);
}
#endif
void BraveVpnService::OnP3AInterval() {
RecordP3A(false);
}
void BraveVpnService::SetPurchasedState(const std::string& env,
PurchasedState state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -1079,7 +1151,7 @@ void BraveVpnService::SetPurchasedState(const std::string& env,
}
void BraveVpnService::SetCurrentEnvironment(const std::string& env) {
prefs_->SetString(prefs::kBraveVPNEEnvironment, env);
profile_prefs_->SetString(prefs::kBraveVPNEEnvironment, env);
purchased_state_.reset();
}
+21 -2
View File
@@ -52,6 +52,10 @@ class BraveBrowserCommandControllerTest;
namespace brave_vpn {
constexpr char kNewUserReturningHistogramName[] = "Brave.VPN.NewUserReturning";
constexpr char kDaysInMonthUsedHistogramName[] = "Brave.VPN.DaysInMonthUsed";
constexpr char kLastUsageTimeHistogramName[] = "Brave.VPN.LastUsageTime";
// This class is used by desktop and android.
// However, it includes desktop specific impls and it's hidden
// by IS_ANDROID ifdef.
@@ -65,7 +69,8 @@ class BraveVpnService :
public:
BraveVpnService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* prefs,
PrefService* local_prefs,
PrefService* profile_prefs,
base::RepeatingCallback<mojo::PendingRemote<skus::mojom::SkusService>()>
skus_service_getter);
~BraveVpnService() override;
@@ -158,8 +163,20 @@ class BraveVpnService :
const std::string& payments_environment,
const std::string& monthly_pass);
// new_usage should be set to true if a new VPN connection was just
// established.
void RecordP3A(bool new_usage);
#if BUILDFLAG(IS_ANDROID)
void RecordAndroidBackgroundP3A(int64_t session_start_time_ms,
int64_t session_end_time_ms);
#endif
private:
friend class BraveVPNServiceTest;
void InitP3A();
void OnP3AInterval();
#if !BUILDFLAG(IS_ANDROID)
friend class ::BraveAppMenuBrowserTest;
friend class ::BraveBrowserCommandControllerTest;
@@ -253,7 +270,8 @@ class BraveVpnService :
const std::string& domain,
const std::string& credential_as_cookie);
raw_ptr<PrefService> prefs_ = nullptr;
raw_ptr<PrefService> local_prefs_ = nullptr;
raw_ptr<PrefService> profile_prefs_ = nullptr;
#if !BUILDFLAG(IS_ANDROID)
std::vector<mojom::Region> regions_;
std::unique_ptr<Hostname> hostname_;
@@ -283,6 +301,7 @@ class BraveVpnService :
mojo::RemoteSet<mojom::ServiceObserver> observers_;
api_request_helper::APIRequestHelper api_request_helper_;
std::string skus_credential_;
base::RepeatingTimer p3a_timer_;
base::WeakPtrFactory<BraveVpnService> weak_ptr_factory_{this};
};
+69 -10
View File
@@ -11,6 +11,7 @@
#include "base/json/json_writer.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "brave/components/brave_vpn/brave_vpn_service.h"
#include "brave/components/brave_vpn/brave_vpn_service_helper.h"
@@ -170,14 +171,20 @@ class TestBraveVPNServiceObserver : public mojom::ServiceObserver {
class BraveVPNServiceTest : public testing::Test {
public:
BraveVPNServiceTest() {
BraveVPNServiceTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
scoped_feature_list_.InitWithFeatures(
{skus::features::kSkusFeature, features::kBraveVPN}, {});
}
void SetUp() override {
skus::RegisterProfilePrefs(pref_service_.registry());
prefs::RegisterProfilePrefs(pref_service_.registry());
base::Time future_mock_time;
if (base::Time::FromString("2023-01-04", &future_mock_time)) {
task_environment_.AdvanceClock(future_mock_time - base::Time::Now());
}
skus::RegisterProfilePrefs(profile_pref_service_.registry());
prefs::RegisterProfilePrefs(profile_pref_service_.registry());
prefs::RegisterLocalStatePrefs(local_pref_service_.registry());
shared_url_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&url_loader_factory_);
@@ -185,17 +192,17 @@ class BraveVPNServiceTest : public testing::Test {
&BraveVPNServiceTest::Interceptor, base::Unretained(this)));
// Setup required for SKU (dependency of VPN)
skus_service_ = std::make_unique<skus::SkusServiceImpl>(
&pref_service_, url_loader_factory_.GetSafeWeakWrapper());
&profile_pref_service_, url_loader_factory_.GetSafeWeakWrapper());
ResetVpnService();
}
void ResetVpnService() {
service_ = std::make_unique<BraveVpnService>(
url_loader_factory_.GetSafeWeakWrapper(), &pref_service_,
url_loader_factory_.GetSafeWeakWrapper(), &local_pref_service_,
&profile_pref_service_,
base::BindRepeating(&BraveVPNServiceTest::GetSkusService,
base::Unretained(this)));
}
PrefService* prefs() { return &pref_service_; }
mojo::PendingRemote<skus::mojom::SkusService> GetSkusService() {
if (!skus_service_) {
return mojo::PendingRemote<skus::mojom::SkusService>();
@@ -314,6 +321,7 @@ class BraveVPNServiceTest : public testing::Test {
}
#endif
void RecordP3A(bool new_usage) { service_->RecordP3A(new_usage); }
std::string GetCurrentEnvironment() {
return service_->GetCurrentEnvironment();
@@ -481,7 +489,7 @@ class BraveVPNServiceTest : public testing::Test {
auto testing_payload = GenerateTestingCreds(domain, active_subscription);
base::Value state(base::Value::Type::DICT);
state.SetStringKey("skus:" + env, testing_payload);
prefs()->Set(skus::prefs::kSkusState, std::move(state));
profile_pref_service_.Set(skus::prefs::kSkusState, std::move(state));
SetInterceptorResponse(GetRegionsData());
return domain;
}
@@ -499,12 +507,14 @@ class BraveVPNServiceTest : public testing::Test {
std::string https_response_;
base::test::ScopedFeatureList scoped_feature_list_;
content::BrowserTaskEnvironment task_environment_;
sync_preferences::TestingPrefServiceSyncable pref_service_;
sync_preferences::TestingPrefServiceSyncable profile_pref_service_;
sync_preferences::TestingPrefServiceSyncable local_pref_service_;
std::unique_ptr<skus::SkusServiceImpl> skus_service_;
std::unique_ptr<BraveVpnService> service_;
network::TestURLLoaderFactory url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
base::HistogramTester histogram_tester_;
};
TEST(BraveVPNFeatureTest, FeatureTest) {
@@ -698,6 +708,7 @@ TEST_F(BraveVPNServiceTest, ConnectionStateUpdateWithPurchasedStateTest) {
UpdateAndNotifyConnectionStateChange(ConnectionState::CONNECTED);
EXPECT_NE(ConnectionState::CONNECTED, connection_state());
}
TEST_F(BraveVPNServiceTest, ConnectionInfoTest) {
// Having skus_credential is pre-requisite before try connecting.
skus_credential() = "test_credentials";
@@ -924,8 +935,8 @@ TEST_F(BraveVPNServiceTest, CheckInitialPurchasedStateTest) {
EXPECT_EQ(PurchasedState::NOT_PURCHASED, GetPurchasedStateSync());
// Dirty region list prefs to pretend it's already cached.
pref_service_.Set(prefs::kBraveVPNRegionList,
base::Value(base::Value::Type::LIST));
profile_pref_service_.Set(prefs::kBraveVPNRegionList,
base::Value(base::Value::Type::LIST));
ResetVpnService();
EXPECT_EQ(PurchasedState::LOADING, GetPurchasedStateSync());
}
@@ -1038,4 +1049,52 @@ TEST_F(BraveVPNServiceTest, LoadPurchasedStateForAnotherEnv) {
EXPECT_EQ(GetCurrentEnvironment(), skus::kEnvStaging);
}
TEST_F(BraveVPNServiceTest, NewUserReturningMetric) {
RecordP3A(false);
histogram_tester_.ExpectBucketCount(kNewUserReturningHistogramName, 0, 2);
task_environment_.FastForwardBy(base::Days(1));
RecordP3A(true);
histogram_tester_.ExpectBucketCount(kNewUserReturningHistogramName, 2, 1);
task_environment_.FastForwardBy(base::Days(1));
RecordP3A(true);
histogram_tester_.ExpectBucketCount(kNewUserReturningHistogramName, 3, 1);
task_environment_.FastForwardBy(base::Days(6));
histogram_tester_.ExpectBucketCount(kNewUserReturningHistogramName, 1, 1);
}
TEST_F(BraveVPNServiceTest, DaysInMonthUsedMetric) {
RecordP3A(false);
histogram_tester_.ExpectTotalCount(kDaysInMonthUsedHistogramName, 0);
RecordP3A(true);
histogram_tester_.ExpectBucketCount(kDaysInMonthUsedHistogramName, 1, 1);
task_environment_.FastForwardBy(base::Days(1));
RecordP3A(true);
histogram_tester_.ExpectBucketCount(kDaysInMonthUsedHistogramName, 2, 1);
task_environment_.FastForwardBy(base::Days(1));
histogram_tester_.ExpectBucketCount(kDaysInMonthUsedHistogramName, 2, 2);
RecordP3A(true);
task_environment_.FastForwardBy(base::Days(30));
histogram_tester_.ExpectBucketCount(kDaysInMonthUsedHistogramName, 0, 1);
}
TEST_F(BraveVPNServiceTest, LastUsageTimeMetric) {
histogram_tester_.ExpectTotalCount(kLastUsageTimeHistogramName, 0);
RecordP3A(true);
histogram_tester_.ExpectBucketCount(kLastUsageTimeHistogramName, 1, 1);
task_environment_.AdvanceClock(base::Days(10));
RecordP3A(true);
histogram_tester_.ExpectBucketCount(kLastUsageTimeHistogramName, 1, 2);
task_environment_.AdvanceClock(base::Days(10));
RecordP3A(false);
histogram_tester_.ExpectBucketCount(kLastUsageTimeHistogramName, 2, 1);
}
} // namespace brave_vpn
+7
View File
@@ -5,6 +5,7 @@
#include "brave/components/brave_vpn/pref_names.h"
#include "brave/components/p3a_utils/feature_usage.h"
#include "brave/components/skus/browser/skus_utils.h"
#include "components/prefs/pref_registry_simple.h"
@@ -32,6 +33,12 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
skus::GetDefaultEnvironment());
}
void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
p3a_utils::RegisterFeatureUsagePrefs(
registry, kBraveVPNFirstUseTime, kBraveVPNLastUseTime,
kBraveVPNUsedSecondDay, kBraveVPNDaysInMonthUsed);
}
} // namespace prefs
} // namespace brave_vpn
+7
View File
@@ -27,7 +27,14 @@ extern const char kBraveVPNPurchaseTokenAndroid[];
extern const char kBraveVPNPackageAndroid[];
#endif
constexpr char kBraveVPNFirstUseTime[] = "brave.brave_vpn.first_use_time";
constexpr char kBraveVPNLastUseTime[] = "brave.brave_vpn.last_use_time";
constexpr char kBraveVPNUsedSecondDay[] = "brave.brave_vpn.used_second_day";
constexpr char kBraveVPNDaysInMonthUsed[] =
"brave.brave_vpn.days_in_month_used";
void RegisterProfilePrefs(PrefRegistrySimple* registry);
void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
} // namespace prefs
+3
View File
@@ -73,6 +73,9 @@ constexpr inline auto kCollectedHistograms =
"Brave.Sync.Status.2",
"Brave.Sync.ProgressTokenEverReset",
"Brave.Uptime.BrowserOpenMinutes",
"Brave.VPN.NewUserReturning",
"Brave.VPN.DaysInMonthUsed",
"Brave.VPN.LastUsageTime",
"Brave.Wallet.DefaultSolanaWalletSetting",
"Brave.Wallet.DefaultWalletSetting",
"Brave.Wallet.KeyringCreated",
+44 -12
View File
@@ -13,6 +13,12 @@
namespace p3a_utils {
namespace {
constexpr int kDaysInMonthBuckets[] = {0, 1, 2, 5, 10, 15, 20, 100};
} // namespace
void RegisterFeatureUsagePrefs(PrefRegistrySimple* registry,
const char* first_use_time_pref_name,
const char* last_use_time_pref_name,
@@ -36,14 +42,23 @@ void RegisterFeatureUsagePrefs(PrefRegistrySimple* registry,
void RecordFeatureUsage(PrefService* prefs,
const char* first_use_time_pref_name,
const char* last_use_time_pref_name) {
RecordFeatureUsage(prefs, first_use_time_pref_name, last_use_time_pref_name,
base::Time::Now());
}
void RecordFeatureUsage(PrefService* prefs,
const char* first_use_time_pref_name,
const char* last_use_time_pref_name,
const base::Time& last_new_use_time) {
DCHECK(prefs);
DCHECK(first_use_time_pref_name);
DCHECK(last_use_time_pref_name);
DCHECK(!last_new_use_time.is_null());
base::Time now_midnight = base::Time::Now().LocalMidnight();
prefs->SetTime(last_use_time_pref_name, now_midnight);
base::Time new_time_midnight = last_new_use_time.LocalMidnight();
prefs->SetTime(last_use_time_pref_name, new_time_midnight);
if (prefs->GetTime(first_use_time_pref_name).is_null()) {
prefs->SetTime(first_use_time_pref_name, now_midnight);
prefs->SetTime(first_use_time_pref_name, new_time_midnight);
}
}
@@ -72,7 +87,8 @@ void RecordFeatureNewUserReturning(PrefService* prefs,
const char* first_use_time_pref_name,
const char* last_use_time_pref_name,
const char* used_second_day_pref_name,
const char* histogram_name) {
const char* histogram_name,
bool write_to_histogram) {
DCHECK(prefs);
DCHECK(first_use_time_pref_name);
DCHECK(last_use_time_pref_name);
@@ -81,6 +97,7 @@ void RecordFeatureNewUserReturning(PrefService* prefs,
base::Time last_use_time = prefs->GetTime(last_use_time_pref_name);
base::Time first_use_time = prefs->GetTime(first_use_time_pref_name);
int answer = 0;
if (!first_use_time.is_null()) {
// If the first use time was set, we can assume that
@@ -109,14 +126,17 @@ void RecordFeatureNewUserReturning(PrefService* prefs,
answer = 4;
}
}
base::UmaHistogramExactLinear(histogram_name, answer, 5);
if (write_to_histogram) {
base::UmaHistogramExactLinear(histogram_name, answer, 5);
}
}
void RecordFeatureDaysInMonthUsed(PrefService* prefs,
bool is_add,
const base::Time& add_date,
const char* last_use_time_pref_name,
const char* days_in_month_used_pref_name,
const char* histogram_name) {
const char* histogram_name,
bool write_to_histogram) {
DCHECK(prefs);
DCHECK(last_use_time_pref_name);
DCHECK(days_in_month_used_pref_name);
@@ -126,13 +146,25 @@ void RecordFeatureDaysInMonthUsed(PrefService* prefs,
// Don't report if feature was never used
return;
}
// How many days was the feature used in the last month?
constexpr int buckets[] = {0, 1, 2, 5, 10, 15, 20, 100};
MonthlyStorage storage(prefs, days_in_month_used_pref_name);
if (is_add) {
storage.ReplaceTodaysValueIfGreater(1);
if (!add_date.is_null()) {
storage.ReplaceIfGreaterForDate(add_date, 1);
}
RecordToHistogramBucket(histogram_name, buckets, storage.GetMonthlySum());
if (write_to_histogram) {
RecordToHistogramBucket(histogram_name, kDaysInMonthBuckets,
storage.GetMonthlySum());
}
}
void RecordFeatureDaysInMonthUsed(PrefService* prefs,
bool is_add,
const char* last_use_time_pref_name,
const char* days_in_month_used_pref_name,
const char* histogram_name,
bool write_to_histogram) {
RecordFeatureDaysInMonthUsed(
prefs, is_add ? base::Time::Now() : base::Time(), last_use_time_pref_name,
days_in_month_used_pref_name, histogram_name, write_to_histogram);
}
void RecordFeatureLastUsageTimeMetric(PrefService* prefs,
+25 -8
View File
@@ -31,6 +31,12 @@ void RecordFeatureUsage(PrefService* prefs,
const char* first_use_time_pref_name,
const char* last_use_time_pref_name);
// Updates the first/last use time preferences using an external timestamp;
void RecordFeatureUsage(PrefService* prefs,
const char* first_use_time_pref_name,
const char* last_use_time_pref_name,
const base::Time& last_new_use_time);
// Assists in migrating existing external last use timestamps that existed
// before this P3A utility was created.
void MaybeRecordFeatureExistingUsageTimestamp(
@@ -57,7 +63,8 @@ void RecordFeatureNewUserReturning(PrefService* prefs,
const char* first_use_time_pref_name,
const char* last_use_time_pref_name,
const char* used_second_day_pref_name,
const char* histogram_name);
const char* histogram_name,
bool write_to_histogram = true);
// Records the DaysInMonthUsed metric. Will not report if feature never used.
//
@@ -76,7 +83,17 @@ void RecordFeatureDaysInMonthUsed(PrefService* prefs,
bool is_add,
const char* last_use_time_pref_name,
const char* days_in_month_used_pref_name,
const char* histogram_name);
const char* histogram_name,
bool write_to_histogram = true);
// Adds feature usage to monthly storage for a provided date,
// and records the DaysInMonthUsed metric.
void RecordFeatureDaysInMonthUsed(PrefService* prefs,
const base::Time& add_date,
const char* last_use_time_pref_name,
const char* days_in_month_used_pref_name,
const char* histogram_name,
bool write_to_histogram = true);
// Records the LastUsageTime metric. Will not report if feature never used.
//
@@ -84,12 +101,12 @@ void RecordFeatureDaysInMonthUsed(PrefService* prefs,
// feature?
//
// Answers:
// 1. This week
// 2. 1 week ago
// 3. 2 weeks ago
// 4. 3 weeks ago
// 5. A month ago
// 6. Over a month ago
// 1. 0 - 6 days ago (less than a week)
// 2. 7 - 13 days ago (one week ago or more)
// 3. 14 - 20 days ago (two weeks ago or more)
// 4. 21 - 27 days ago (three weeks ago or more)
// 5. 28 - 59 days ago (four weeks ago or more)
// 6. 60 days ago or more (two months ago or more)
void RecordFeatureLastUsageTimeMetric(PrefService* prefs,
const char* last_use_time_pref_name,
const char* histogram_name);
@@ -9,6 +9,7 @@
#include <numeric>
#include <utility>
#include "base/ranges/algorithm.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "base/values.h"
@@ -71,6 +72,24 @@ void TimePeriodStorage::ReplaceTodaysValueIfGreater(uint64_t value) {
Save();
}
void TimePeriodStorage::ReplaceIfGreaterForDate(const base::Time& date,
uint64_t value) {
FilterToPeriod();
base::Time date_mn = date.LocalMidnight();
std::list<DailyValue>::iterator day_insert_it = base::ranges::find_if(
daily_values_.begin(), daily_values_.end(),
[date_mn](const DailyValue& val) { return val.day <= date_mn; });
if (day_insert_it != daily_values_.end() && day_insert_it->day == date_mn) {
// update daily value if it exists for date
if (value > day_insert_it->value) {
day_insert_it->value = value;
}
} else {
daily_values_.insert(day_insert_it, {date_mn, value});
}
Save();
}
uint64_t TimePeriodStorage::GetPeriodSum() const {
// We record only value for last N days.
const base::Time n_days_ago = clock_->Now() - base::Days(period_days_);
@@ -39,6 +39,7 @@ class TimePeriodStorage {
void AddDelta(uint64_t delta);
void SubDelta(uint64_t delta);
void ReplaceTodaysValueIfGreater(uint64_t value);
void ReplaceIfGreaterForDate(const base::Time& date, uint64_t value);
uint64_t GetPeriodSum() const;
uint64_t GetHighestValueInPeriod() const;
bool IsOnePeriodPassed() const;
@@ -214,3 +214,29 @@ TEST_F(TimePeriodStorageTest, GetsHighestValueInWeekFromReplacement) {
// Sanity check disparate days were not replaced
EXPECT_EQ(state_->GetPeriodSum(), high_value + low_value);
}
TEST_F(TimePeriodStorageTest, ReplaceIfGreaterForDate) {
InitStorage(30);
state_->AddDelta(4);
clock_->Advance(base::Days(1));
state_->AddDelta(2);
clock_->Advance(base::Days(1));
state_->AddDelta(1);
clock_->Advance(base::Days(1));
// should replace
state_->ReplaceIfGreaterForDate(clock_->Now() - base::Days(2), 3);
// should not replace
state_->ReplaceIfGreaterForDate(clock_->Now() - base::Days(3), 3);
EXPECT_EQ(state_->GetPeriodSum(), 8U);
// should insert new daily value
state_->ReplaceIfGreaterForDate(clock_->Now() - base::Days(4), 3);
EXPECT_EQ(state_->GetPeriodSum(), 11U);
// should store, but should not be in sum because it's too old
state_->ReplaceIfGreaterForDate(clock_->Now() - base::Days(31), 10);
EXPECT_EQ(state_->GetPeriodSum(), 11U);
}