From 89fb27c78375846f3683282bd5e7751cbbec152e Mon Sep 17 00:00:00 2001 From: Simon Hong Date: Mon, 11 Apr 2022 12:48:25 +0900 Subject: [PATCH] Separated some helper/parsers method to helper.h --- components/brave_vpn/BUILD.gn | 2 + components/brave_vpn/brave_vpn_constants.h | 7 + components/brave_vpn/brave_vpn_service.cc | 210 ++---------------- components/brave_vpn/brave_vpn_service.h | 6 - .../brave_vpn/brave_vpn_service_helper.cc | 184 +++++++++++++++ .../brave_vpn/brave_vpn_service_helper.h | 40 ++++ components/brave_vpn/brave_vpn_unittest.cc | 7 +- 7 files changed, 254 insertions(+), 202 deletions(-) create mode 100644 components/brave_vpn/brave_vpn_service_helper.cc create mode 100644 components/brave_vpn/brave_vpn_service_helper.h diff --git a/components/brave_vpn/BUILD.gn b/components/brave_vpn/BUILD.gn index 7bf75ed4e404..ed7689fbf91f 100644 --- a/components/brave_vpn/BUILD.gn +++ b/components/brave_vpn/BUILD.gn @@ -50,6 +50,8 @@ static_library("brave_vpn") { "brave_vpn_os_connection_api.h", "brave_vpn_os_connection_api_sim.cc", "brave_vpn_os_connection_api_sim.h", + "brave_vpn_service_helper.cc", + "brave_vpn_service_helper.h", "pref_names.cc", "pref_names.h", "switches.h", diff --git a/components/brave_vpn/brave_vpn_constants.h b/components/brave_vpn/brave_vpn_constants.h index 778756097acd..94d2ccf3bc77 100644 --- a/components/brave_vpn/brave_vpn_constants.h +++ b/components/brave_vpn/brave_vpn_constants.h @@ -76,6 +76,13 @@ constexpr char kManageUrlDev[] = "https://account.brave.software/"; // TODO(simonhong): Update when vpn feedback url is ready. constexpr char kFeedbackUrl[] = "http://support.brave.com/"; constexpr char kAboutUrl[] = "https://brave.com/firewall-vpn/"; + +constexpr char kBraveVPNEntryName[] = "BraveVPN"; +constexpr char kRegionContinentKey[] = "continent"; +constexpr char kRegionNameKey[] = "name"; +constexpr char kRegionNamePrettyKey[] = "name-pretty"; +constexpr char kCreateSupportTicket[] = "api/v1.2/partners/support-ticket"; + } // namespace brave_vpn #endif // BRAVE_COMPONENTS_BRAVE_VPN_BRAVE_VPN_CONSTANTS_H_ diff --git a/components/brave_vpn/brave_vpn_service.cc b/components/brave_vpn/brave_vpn_service.cc index f4f302baca20..ca76eb7a557e 100644 --- a/components/brave_vpn/brave_vpn_service.cc +++ b/components/brave_vpn/brave_vpn_service.cc @@ -18,7 +18,6 @@ #include "url/url_util.h" #if !BUILDFLAG(IS_ANDROID) -#include "base/base64.h" #include "base/bind.h" #include "base/command_line.h" #include "base/logging.h" @@ -27,6 +26,7 @@ #include "base/strings/string_util.h" #include "base/values.h" #include "brave/components/brave_vpn/brave_vpn_constants.h" +#include "brave/components/brave_vpn/brave_vpn_service_helper.h" #include "brave/components/brave_vpn/brave_vpn_utils.h" #include "brave/components/brave_vpn/pref_names.h" #include "brave/components/brave_vpn/switches.h" @@ -37,55 +37,8 @@ #include "third_party/icu/source/i18n/unicode/timezone.h" #endif // !BUILDFLAG(IS_ANDROID) -using ConnectionState = brave_vpn::mojom::ConnectionState; -using PurchasedState = brave_vpn::mojom::PurchasedState; - namespace { -#if !BUILDFLAG(IS_ANDROID) -constexpr char kBraveVPNEntryName[] = "BraveVPN"; - -constexpr char kRegionContinentKey[] = "continent"; -constexpr char kRegionNameKey[] = "name"; -constexpr char kRegionNamePrettyKey[] = "name-pretty"; - -constexpr char kCreateSupportTicket[] = "api/v1.2/partners/support-ticket"; - -bool IsValidRegionValue(const base::Value& value) { - if (!value.FindStringKey(kRegionContinentKey) || - !value.FindStringKey(kRegionNameKey) || - !value.FindStringKey(kRegionNamePrettyKey)) { - return false; - } - - return true; -} - -// On desktop, the environment is tied to SKUs because you would purchase it -// from `account.brave.com` (or similar, based on env). The credentials for VPN -// will always be in the same environment as the SKU environment. -// -// When the vendor receives a credential from us during auth, it also includes -// the environment. The vendor then can do a lookup using Payment Service. -std::string GetBraveVPNPaymentsEnv() { - const std::string env = skus::GetEnvironment(); - if (env == skus::kEnvProduction) - return ""; - // Use same value. - if (env == skus::kEnvStaging || env == skus::kEnvDevelopment) - return env; - - NOTREACHED(); - -#if defined(OFFICIAL_BUILD) - return ""; -#else - return "development"; -#endif -} - -#endif // !BUILDFLAG(IS_ANDROID) - constexpr char kVpnHost[] = "connect-api.guardianapp.com"; constexpr char kAllServerRegions[] = "api/v1/servers/all-server-regions"; @@ -156,6 +109,9 @@ std::string GetSubscriberCredentialFromJson(const std::string& json) { namespace brave_vpn { +using ConnectionState = mojom::ConnectionState; +using PurchasedState = mojom::PurchasedState; + #if BUILDFLAG(IS_ANDROID) BraveVpnService::BraveVpnService( scoped_refptr url_loader_factory, @@ -537,38 +493,6 @@ void BraveVpnService::OnFetchRegionList(bool background_fetch, } } -bool BraveVpnService::ValidateCachedRegionData( - const base::Value& region_value) const { - for (const auto& value : region_value.GetList()) { - // Make sure cached one has all latest properties. - if (!IsValidRegionValue(value)) - return false; - } - - return true; -} - -base::Value BraveVpnService::GetValueFromRegion( - const mojom::Region& region) const { - base::Value region_dict(base::Value::Type::DICTIONARY); - region_dict.SetStringKey(kRegionContinentKey, region.continent); - region_dict.SetStringKey(kRegionNameKey, region.name); - region_dict.SetStringKey(kRegionNamePrettyKey, region.name_pretty); - return region_dict; -} - -mojom::Region BraveVpnService::GetRegionFromValue( - const base::Value& value) const { - mojom::Region region; - if (auto* continent = value.FindStringKey(kRegionContinentKey)) - region.continent = *continent; - if (auto* name = value.FindStringKey(kRegionNameKey)) - region.name = *name; - if (auto* name_pretty = value.FindStringKey(kRegionNamePrettyKey)) - region.name_pretty = *name_pretty; - return region; -} - bool BraveVpnService::ParseAndCacheRegionList(const base::Value& region_value, bool save_to_prefs) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -576,20 +500,7 @@ bool BraveVpnService::ParseAndCacheRegionList(const base::Value& region_value, if (!region_value.is_list()) return false; - regions_.clear(); - for (const auto& value : region_value.GetList()) { - DCHECK(value.is_dict()); - if (!value.is_dict()) - continue; - regions_.push_back(GetRegionFromValue(value)); - } - - // Sort region list alphabetically - std::sort(regions_.begin(), regions_.end(), - [](mojom::Region& a, mojom::Region& b) { - return (a.name_pretty < b.name_pretty); - }); - + regions_ = ParseRegionList(region_value); VLOG(2) << __func__ << " : has regionlist: " << !regions_.empty(); // If we can't get region list, we can't determine device region. @@ -701,25 +612,13 @@ void BraveVpnService::GetAllRegions(GetAllRegionsCallback callback) { std::move(callback).Run(std::move(regions)); } -mojom::Region BraveVpnService::GetRegionWithName( - const std::string& name) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - mojom::Region region; - auto it = - std::find_if(regions_.begin(), regions_.end(), - [&name](const auto& region) { return region.name == name; }); - if (it != regions_.end()) - region = *it; - return region; -} - void BraveVpnService::GetDeviceRegion(GetDeviceRegionCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); VLOG(2) << __func__; auto region_name = GetDeviceRegion(); DCHECK(!region_name.empty()); - auto region = GetRegionWithName(region_name); - std::move(callback).Run(region.Clone()); + std::move(callback).Run( + GetRegionPtrWithNameFromRegionList(region_name, regions_)); } void BraveVpnService::GetSelectedRegion(GetSelectedRegionCallback callback) { @@ -733,9 +632,8 @@ void BraveVpnService::GetSelectedRegion(GetSelectedRegionCallback callback) { region_name = GetDeviceRegion(); } DCHECK(!region_name.empty()); - auto region = GetRegionWithName(region_name); - VLOG(2) << __func__ << " : Give " << region.name_pretty; - std::move(callback).Run(region.Clone()); + std::move(callback).Run( + GetRegionPtrWithNameFromRegionList(region_name, regions_)); } void BraveVpnService::SetSelectedRegion(mojom::RegionPtr region_ptr) { @@ -771,28 +669,10 @@ void BraveVpnService::CreateSupportTicket( base::BindOnce(&BraveVpnService::OnCreateSupportTicket, weak_ptr_factory_.GetWeakPtr(), std::move(callback)); - const GURL base_url = GetURLWithPath(kVpnHost, kCreateSupportTicket); - base::Value dict(base::Value::Type::DICTIONARY); - - std::string email_trimmed, subject_trimmed, body_trimmed, body_encoded; - base::TrimWhitespaceASCII(email, base::TRIM_ALL, &email_trimmed); - base::TrimWhitespaceASCII(subject, base::TRIM_ALL, &subject_trimmed); - base::TrimWhitespaceASCII(body, base::TRIM_ALL, &body_trimmed); - base::Base64Encode(body_trimmed, &body_encoded); - - // required fields - dict.SetStringKey("email", email_trimmed); - dict.SetStringKey("subject", subject_trimmed); - dict.SetStringKey("support-ticket", body_encoded); - dict.SetStringKey("partner-client-id", "com.brave.browser"); - - // optional (but encouraged) fields - dict.SetStringKey("subscriber-credential", ""); - dict.SetStringKey("payment-validation-method", "brave-premium"); - dict.SetStringKey("payment-validation-data", ""); - - std::string request_body = CreateJSONRequestBody(dict); - OAuthRequest(base_url, "POST", request_body, std::move(internal_callback)); + OAuthRequest( + GetURLWithPath(kVpnHost, kCreateSupportTicket), "POST", + CreateJSONRequestBody(GetValueWithTicketInfos(email, subject, body)), + std::move(internal_callback)); } void BraveVpnService::GetSupportData(GetSupportDataCallback callback) { @@ -853,30 +733,7 @@ void BraveVpnService::ParseAndCacheHostnames( return; } - constexpr char kHostnameKey[] = "hostname"; - constexpr char kDisplayNameKey[] = "display-name"; - constexpr char kOfflineKey[] = "offline"; - constexpr char kCapacityScoreKey[] = "capacity-score"; - - std::vector hostnames; - for (const auto& value : hostnames_value.GetList()) { - DCHECK(value.is_dict()); - if (!value.is_dict()) - continue; - - const std::string* hostname_str = value.FindStringKey(kHostnameKey); - const std::string* display_name_str = value.FindStringKey(kDisplayNameKey); - absl::optional offline = value.FindBoolKey(kOfflineKey); - absl::optional capacity_score = value.FindIntKey(kCapacityScoreKey); - - if (!hostname_str || !display_name_str || !offline || !capacity_score) - continue; - - hostnames.push_back( - Hostname{*hostname_str, *display_name_str, *offline, *capacity_score}); - } - - VLOG(2) << __func__ << " : has hostname: " << !hostnames.empty(); + std::vector hostnames = ParseHostnames(hostnames_value); if (hostnames.empty()) { VLOG(2) << __func__ << " : got empty hostnames list for " << region; @@ -977,26 +834,6 @@ void BraveVpnService::OnGetProfileCredentials( UpdateAndNotifyConnectionStateChange(ConnectionState::CONNECT_FAILED); } -std::unique_ptr BraveVpnService::PickBestHostname( - const std::vector& hostnames) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - std::vector filtered_hostnames; - std::copy_if(hostnames.begin(), hostnames.end(), - std::back_inserter(filtered_hostnames), - [](const Hostname& hostname) { return !hostname.is_offline; }); - - std::sort(filtered_hostnames.begin(), filtered_hostnames.end(), - [](const Hostname& a, const Hostname& b) { - return a.capacity_score > b.capacity_score; - }); - - if (filtered_hostnames.empty()) - return std::make_unique(); - - // Pick highest capacity score. - return std::make_unique(filtered_hostnames[0]); -} - BraveVPNOSConnectionAPI* BraveVpnService::GetBraveVPNConnectionAPI() { if (is_simulation_) return BraveVPNOSConnectionAPI::GetInstanceForTest(); @@ -1156,24 +993,7 @@ void BraveVpnService::SetPurchasedState(PurchasedState state) { return; purchased_state_ = state; - - switch (state) { - case PurchasedState::NOT_PURCHASED: - VLOG(1) << "SetPurchasedState(NOT_PURCHASED);"; - break; - case PurchasedState::PURCHASED: - VLOG(1) << "SetPurchasedState(PURCHASED);"; - break; - case PurchasedState::EXPIRED: - VLOG(1) << "SetPurchasedState(EXPIRED);"; - break; - case PurchasedState::LOADING: - VLOG(1) << "SetPurchasedState(LOADING);"; - break; - case PurchasedState::FAILED: - VLOG(1) << "SetPurchasedState(FAILED);"; - break; - } + VLOG(1) << "SetPurchasedState: " << purchased_state_; for (const auto& obs : observers_) obs->OnPurchasedStateChanged(purchased_state_); diff --git a/components/brave_vpn/brave_vpn_service.h b/components/brave_vpn/brave_vpn_service.h index 65289aac3fd3..e7977267dec5 100644 --- a/components/brave_vpn/brave_vpn_service.h +++ b/components/brave_vpn/brave_vpn_service.h @@ -171,7 +171,6 @@ class BraveVpnService : bool success); bool ParseAndCacheRegionList(const base::Value& region_value, bool save_to_prefs = false); - bool ValidateCachedRegionData(const base::Value& region_value) const; void OnFetchTimezones(const std::string& timezones_list, bool success); void SetDeviceRegionWithTimezone(const base::Value& timezons_value); void FetchHostnamesForRegion(const std::string& name); @@ -184,17 +183,12 @@ class BraveVpnService : void SetSelectedRegion(const std::string& name); std::string GetDeviceRegion() const; std::string GetSelectedRegion() const; - mojom::Region GetRegionWithName(const std::string& name) const; void SetFallbackDeviceRegion(); void SetRegionListToPrefs(); - mojom::Region GetRegionFromValue(const base::Value& value) const; - base::Value GetValueFromRegion(const mojom::Region& region) const; std::string GetCurrentTimeZone(); void ScheduleBackgroundRegionDataFetch(); void ScheduleFetchRegionDataIfNeeded(); - std::unique_ptr PickBestHostname( - const std::vector& hostnames); void OnGetSubscriberCredentialV12(const std::string& subscriber_credential, bool success); diff --git a/components/brave_vpn/brave_vpn_service_helper.cc b/components/brave_vpn/brave_vpn_service_helper.cc new file mode 100644 index 000000000000..2f0c5bd44497 --- /dev/null +++ b/components/brave_vpn/brave_vpn_service_helper.cc @@ -0,0 +1,184 @@ +/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/brave_vpn/brave_vpn_service_helper.h" + +#include + +#include "base/base64.h" +#include "base/notreached.h" +#include "base/values.h" +#include "brave/components/brave_vpn/brave_vpn_constants.h" +#include "brave/components/brave_vpn/brave_vpn_data_types.h" +#include "brave/components/skus/browser/skus_utils.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace brave_vpn { + +namespace { + +bool IsValidRegionValue(const base::Value& value) { + if (!value.FindStringKey(kRegionContinentKey) || + !value.FindStringKey(kRegionNameKey) || + !value.FindStringKey(kRegionNamePrettyKey)) { + return false; + } + + return true; +} + +mojom::Region GetRegionFromValue(const base::Value& value) { + mojom::Region region; + if (auto* continent = value.FindStringKey(kRegionContinentKey)) + region.continent = *continent; + if (auto* name = value.FindStringKey(kRegionNameKey)) + region.name = *name; + if (auto* name_pretty = value.FindStringKey(kRegionNamePrettyKey)) + region.name_pretty = *name_pretty; + return region; +} + +} // namespace + +// On desktop, the environment is tied to SKUs because you would purchase it +// from `account.brave.com` (or similar, based on env). The credentials for VPN +// will always be in the same environment as the SKU environment. +// +// When the vendor receives a credential from us during auth, it also includes +// the environment. The vendor then can do a lookup using Payment Service. +std::string GetBraveVPNPaymentsEnv() { + const std::string env = skus::GetEnvironment(); + if (env == skus::kEnvProduction) + return ""; + // Use same value. + if (env == skus::kEnvStaging || env == skus::kEnvDevelopment) + return env; + + NOTREACHED(); + +#if defined(OFFICIAL_BUILD) + return ""; +#else + return "development"; +#endif +} + +bool ValidateCachedRegionData(const base::Value& region_value) { + for (const auto& value : region_value.GetList()) { + // Make sure cached one has all latest properties. + if (!IsValidRegionValue(value)) + return false; + } + + return true; +} + +base::Value GetValueFromRegion(const mojom::Region& region) { + base::Value region_dict(base::Value::Type::DICTIONARY); + region_dict.SetStringKey(kRegionContinentKey, region.continent); + region_dict.SetStringKey(kRegionNameKey, region.name); + region_dict.SetStringKey(kRegionNamePrettyKey, region.name_pretty); + return region_dict; +} + +std::unique_ptr PickBestHostname( + const std::vector& hostnames) { + std::vector filtered_hostnames; + std::copy_if(hostnames.begin(), hostnames.end(), + std::back_inserter(filtered_hostnames), + [](const Hostname& hostname) { return !hostname.is_offline; }); + + std::sort(filtered_hostnames.begin(), filtered_hostnames.end(), + [](const Hostname& a, const Hostname& b) { + return a.capacity_score > b.capacity_score; + }); + + if (filtered_hostnames.empty()) + return std::make_unique(); + + // Pick highest capacity score. + return std::make_unique(filtered_hostnames[0]); +} + +std::vector ParseHostnames(const base::Value& hostnames_value) { + std::vector hostnames; + for (const auto& value : hostnames_value.GetList()) { + DCHECK(value.is_dict()); + if (!value.is_dict()) + continue; + + constexpr char kHostnameKey[] = "hostname"; + constexpr char kDisplayNameKey[] = "display-name"; + constexpr char kOfflineKey[] = "offline"; + constexpr char kCapacityScoreKey[] = "capacity-score"; + const std::string* hostname_str = value.FindStringKey(kHostnameKey); + const std::string* display_name_str = value.FindStringKey(kDisplayNameKey); + absl::optional offline = value.FindBoolKey(kOfflineKey); + absl::optional capacity_score = value.FindIntKey(kCapacityScoreKey); + + if (!hostname_str || !display_name_str || !offline || !capacity_score) + continue; + + hostnames.push_back( + Hostname{*hostname_str, *display_name_str, *offline, *capacity_score}); + } + + return hostnames; +} + +std::vector ParseRegionList(const base::Value& region_list) { + std::vector regions; + for (const auto& value : region_list.GetList()) { + DCHECK(value.is_dict()); + if (!value.is_dict()) + continue; + regions.push_back(GetRegionFromValue(value)); + } + + // Sort region list alphabetically + std::sort(regions.begin(), regions.end(), + [](mojom::Region& a, mojom::Region& b) { + return (a.name_pretty < b.name_pretty); + }); + return regions; +} + +base::Value GetValueWithTicketInfos(const std::string& email, + const std::string& subject, + const std::string& body) { + base::Value dict(base::Value::Type::DICTIONARY); + + std::string email_trimmed, subject_trimmed, body_trimmed, body_encoded; + base::TrimWhitespaceASCII(email, base::TRIM_ALL, &email_trimmed); + base::TrimWhitespaceASCII(subject, base::TRIM_ALL, &subject_trimmed); + base::TrimWhitespaceASCII(body, base::TRIM_ALL, &body_trimmed); + base::Base64Encode(body_trimmed, &body_encoded); + + // required fields + dict.SetStringKey("email", email_trimmed); + dict.SetStringKey("subject", subject_trimmed); + dict.SetStringKey("support-ticket", body_encoded); + dict.SetStringKey("partner-client-id", "com.brave.browser"); + + // optional (but encouraged) fields + dict.SetStringKey("subscriber-credential", ""); + dict.SetStringKey("payment-validation-method", "brave-premium"); + dict.SetStringKey("payment-validation-data", ""); + + return dict; +} + +mojom::RegionPtr GetRegionPtrWithNameFromRegionList( + const std::string& name, + const std::vector region_list) { + auto it = + std::find_if(region_list.begin(), region_list.end(), + [&name](const auto& region) { return region.name == name; }); + if (it != region_list.end()) + return it->Clone(); + return mojom::RegionPtr(); +} + +} // namespace brave_vpn diff --git a/components/brave_vpn/brave_vpn_service_helper.h b/components/brave_vpn/brave_vpn_service_helper.h new file mode 100644 index 000000000000..38764c5164ac --- /dev/null +++ b/components/brave_vpn/brave_vpn_service_helper.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_BRAVE_VPN_BRAVE_VPN_SERVICE_HELPER_H_ +#define BRAVE_COMPONENTS_BRAVE_VPN_BRAVE_VPN_SERVICE_HELPER_H_ + +#include +#include +#include + +#include "brave/components/brave_vpn/mojom/brave_vpn.mojom.h" + +namespace base { +class Value; +} // namespace base + +namespace brave_vpn { + +struct Hostname; + +bool ValidateCachedRegionData(const base::Value& region_value); +std::string GetBraveVPNPaymentsEnv(); + +base::Value GetValueFromRegion(const mojom::Region& region); +std::unique_ptr PickBestHostname( + const std::vector& hostnames); +std::vector ParseHostnames(const base::Value& hostnames); +std::vector ParseRegionList(const base::Value& region_list); +base::Value GetValueWithTicketInfos(const std::string& email, + const std::string& subject, + const std::string& body); +mojom::RegionPtr GetRegionPtrWithNameFromRegionList( + const std::string& name, + const std::vector region_list); + +} // namespace brave_vpn + +#endif // BRAVE_COMPONENTS_BRAVE_VPN_BRAVE_VPN_SERVICE_HELPER_H_ diff --git a/components/brave_vpn/brave_vpn_unittest.cc b/components/brave_vpn/brave_vpn_unittest.cc index 45567fad0561..33eb8b7c9c46 100644 --- a/components/brave_vpn/brave_vpn_unittest.cc +++ b/components/brave_vpn/brave_vpn_unittest.cc @@ -9,6 +9,7 @@ #include "base/memory/scoped_refptr.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" #include "brave/components/brave_vpn/brave_vpn_utils.h" #include "brave/components/brave_vpn/features.h" #include "brave/components/brave_vpn/pref_names.h" @@ -84,7 +85,11 @@ class BraveVPNServiceTest : public testing::Test { std::vector& regions() const { return service_->regions_; } mojom::Region device_region() const { - return service_->GetRegionWithName(service_->GetDeviceRegion()); + if (auto region_ptr = GetRegionPtrWithNameFromRegionList( + service_->GetDeviceRegion(), regions())) { + return *region_ptr; + } + return mojom::Region(); } std::unique_ptr& hostname() { return service_->hostname_; }