diff --git a/browser/brave_ads/BUILD.gn b/browser/brave_ads/BUILD.gn index f061e10f1e71..88c2ebee2dae 100644 --- a/browser/brave_ads/BUILD.gn +++ b/browser/brave_ads/BUILD.gn @@ -63,6 +63,7 @@ source_set("impl") { "//brave/components/brave_ads/browser/application_state", "//brave/components/l10n/common", "//brave/components/p3a_utils", + "//brave/components/skus/browser", "//chrome/browser:browser_process", "//chrome/browser:browser_public_dependencies", "//chrome/browser:primitives", diff --git a/browser/brave_ads/ads_service_delegate.cc b/browser/brave_ads/ads_service_delegate.cc index 2bb237dda2ba..ec9d00503458 100644 --- a/browser/brave_ads/ads_service_delegate.cc +++ b/browser/brave_ads/ads_service_delegate.cc @@ -5,8 +5,10 @@ #include "brave/browser/brave_ads/ads_service_delegate.h" +#include #include +#include "base/json/json_reader.h" #include "base/strings/utf_string_conversions.h" #include "base/version_info/channel.h" #include "base/version_info/version_info.h" @@ -15,6 +17,7 @@ #include "brave/browser/ui/brave_ads/notification_ad.h" #include "brave/components/brave_adaptive_captcha/brave_adaptive_captcha_service.h" #include "brave/components/l10n/common/locale_util.h" +#include "brave/components/skus/browser/pref_names.h" #include "build/build_config.h" #include "chrome/browser/notifications/notification_display_service.h" #include "chrome/browser/profiles/profile.h" @@ -34,6 +37,62 @@ namespace brave_ads { +namespace { + +std::string NormalizeSkuEnvironment(const std::string& environment) { + const size_t pos = environment.find(':'); + return environment.substr(pos + 1); +} + +std::string NormalizeSkuStatus(const std::string& status) { + return status == "cancelled" ? "canceled" : status; +} + +base::Value::Dict ParseSkuOrder(const base::Value::Dict& dict) { + base::Value::Dict order; + + if (const auto* const created_at = dict.FindString("created_at")) { + order.Set("created_at", *created_at); + } + + if (const auto* const expires_at = dict.FindString("expires_at")) { + order.Set("expires_at", *expires_at); + } + + if (const auto* const last_paid_at = dict.FindString("last_paid_at")) { + order.Set("last_paid_at", *last_paid_at); + } + + if (const auto* const status = dict.FindString("status")) { + const std::string normalized_status = NormalizeSkuStatus(*status); + order.Set("status", normalized_status); + } + + return order; +} + +base::Value::Dict ParseSkuOrders(const base::Value::Dict& dict) { + base::Value::Dict orders; + + for (const auto [/*id*/ _, value] : dict) { + const base::Value::Dict* const order = value.GetIfDict(); + if (!order) { + continue; + } + + const std::string* const location = order->FindString("location"); + if (!location) { + continue; + } + + orders.Set(*location, ParseSkuOrder(*order)); + } + + return orders; +} + +} // namespace + AdsServiceDelegate::AdsServiceDelegate( Profile* profile, PrefService* local_state, @@ -65,6 +124,39 @@ std::string AdsServiceDelegate::GetDefaultSearchEngineName() { return base::UTF16ToUTF8(default_search_engine_name); } +base::Value::Dict AdsServiceDelegate::GetSkus() const { + base::Value::Dict skus; + + if (!local_state_->HasPrefPath(skus::prefs::kSkusState)) { + // No SKUs in local state. + return skus; + } + + const base::Value::Dict& skus_state = + local_state_->GetDict(skus::prefs::kSkusState); + for (const auto [environment, value] : skus_state) { + if (!environment.starts_with("skus:")) { + continue; + } + + // Convert to Value as it's stored as string in local state. + const std::optional sku_state = + base::JSONReader::ReadDict(value.GetString()); + if (!sku_state) { + continue; + } + + const base::Value::Dict* const orders = sku_state->FindDict("orders"); + if (!orders) { + continue; + } + + skus.Set(NormalizeSkuEnvironment(environment), ParseSkuOrders(*orders)); + } + + return skus; +} + void AdsServiceDelegate::OpenNewTabWithUrl(const GURL& url) { #if BUILDFLAG(IS_ANDROID) // ServiceTabLauncher can currently only launch new tabs @@ -158,15 +250,24 @@ bool AdsServiceDelegate::IsFullScreenMode() { base::Value::Dict AdsServiceDelegate::GetVirtualPrefs() { return base::Value::Dict() - .Set("[virtual]:operating_system.name", version_info::GetOSType()) - .Set("[virtual]:operating_system.locale", - brave_l10n::GetDefaultLocaleString()) - .Set("[virtual]:application_locale", application_locale_) - .Set("[virtual]:build_channel.name", - version_info::GetChannelString(chrome::GetChannel())) - .Set("[virtual]:browser_version", version_info::GetVersionNumber()) - .Set("[virtual]:default_search_engine.name", - GetDefaultSearchEngineName()); + .Set("[virtual]:browser", + base::Value::Dict() + .Set("build_channel", + version_info::GetChannelString(chrome::GetChannel())) + .Set("version", version_info::GetVersionNumber())) + .Set("[virtual]:operating_system", + base::Value::Dict() + .Set("locale", + base::Value::Dict() + .Set("language", + brave_l10n::GetDefaultISOLanguageCodeString()) + .Set("region", + brave_l10n::GetDefaultISOCountryCodeString())) + .Set("name", version_info::GetOSType())) + .Set( + "[virtual]:search_engine", + base::Value::Dict().Set("default_name", GetDefaultSearchEngineName())) + .Set("[virtual]:skus", GetSkus()); } } // namespace brave_ads diff --git a/browser/brave_ads/ads_service_delegate.h b/browser/brave_ads/ads_service_delegate.h index fafb671812c2..c2240d2e5ae0 100644 --- a/browser/brave_ads/ads_service_delegate.h +++ b/browser/brave_ads/ads_service_delegate.h @@ -49,6 +49,8 @@ class AdsServiceDelegate : public AdsService::Delegate { std::string GetDefaultSearchEngineName(); + base::Value::Dict GetSkus() const; + // AdsService::Delegate implementation void InitNotificationHelper() override; bool CanShowSystemNotificationsWhileBrowserIsBackgrounded() override; diff --git a/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h b/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h index fc1b6c4b0430..c28d50481c61 100644 --- a/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h +++ b/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h @@ -65,7 +65,7 @@ class PrefProviderInterface; // - 'R≥': Greater than or equal to // - 'R<': Less than // - 'R≤': Less than or equal to -// - This matcher triggers an ad based on when a real number stored a1t +// - This matcher triggers an ad based on when a real number stored at // "prefPath". For instance, the example below will serve an ad only if the // value stored at "foo.bar" is not equal to 3: // @@ -129,22 +129,41 @@ class PrefProviderInterface; // local state prefs. Virtual pref path keys should be prefixed with // "[virtual]:". // -// "[virtual]:operating_system.name" retrieves the operating system, returning -// one of the following values: "Windows", "Mac OS X", "Linux", "Android", -// "iOS", or "Unknown". +// "[virtual]:browser_version" retrieves the browser version, e.g. "72.0.59.3". // // "[virtual]:build_channel.name" retrieves the build channel of the browser, // returning one of the following values: "stable", "beta", "dev", "nightly", or // "unknown". // -// "[virtual]:browser_version" retrieves the browser version, e.g. "6.0.490.1". +// "[virtual]:operating_system.locale" retrieves the operating system locale, +// i.e., "en_US". +// +// "[virtual]:operating_system.name" retrieves the operating system, returning +// one of the following values: "Windows", "Mac OS X", "Linux", "Android", +// "iOS", or "Unknown". // -// "[virtual]:default_search_engine.name" retrieves the default search engine +// "[virtual]:search_engine.default_name" retrieves the default search engine // chosen during browser installation, returning one of the following values: // "Brave", "Google", "Yandex", "Bing", "Daum", "네이버", "DuckDuckGo", "Qwant", // "Startpage", or "Ecosia". For the most up-to-date list of possible default // search engines, see `TemplateURLPrepopulateData::GetDefaultSearchEngine`. // +// "[virtual]:skus" retrieves the SKUs for the browser, returning either +// `trial`, `beta`, `paid`, or `canceled` for status, i.e., +// +// "production": { +// "talk.brave.com": { +// "created_at": "2023-05-05T12:26:57", +// "expires_at": "2024-05-04T13:44:55", +// "status": "paid" +// }, +// "vpn.brave.com": { +// "created_at": "2023-02-23T21:49:34", +// "expires_at": "2024-09-24T21:49:57", +// "status": "canceled" +// } +// } +// // NOTE: To identify condition matchers, first create a copy of your pref files. // Next, change a brave://setting or enable a feature, quit the browser and then // compare the original and modified versions to determine which key/value pairs