| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <jni.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/android/scoped_java_ref.h" |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/net/dns_probe_runner.h" |
| #include "chrome/browser/net/secure_dns_config.h" |
| #include "chrome/browser/net/secure_dns_util.h" |
| #include "chrome/browser/net/stub_resolver_config_reader.h" |
| #include "chrome/browser/net/system_network_context_manager.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/country_codes/country_codes.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "net/dns/public/dns_over_https_config.h" |
| #include "net/dns/public/doh_provider_entry.h" |
| #include "net/dns/public/secure_dns_mode.h" |
| |
| // Must come after all headers that specialize FromJniType() / ToJniType(). |
| #include "chrome/browser/privacy/jni_headers/SecureDnsBridge_jni.h" |
| |
| using base::android::JavaParamRef; |
| using base::android::ScopedJavaLocalRef; |
| using chrome_browser_net::DnsProbeRunner; |
| |
| namespace secure_dns = chrome_browser_net::secure_dns; |
| |
| namespace { |
| |
| net::DohProviderEntry::List GetFilteredProviders() { |
| // Note: Check whether each provider is enabled *after* filtering based on |
| // country code so that if we are doing experimentation via Finch for a |
| // regional provider, the experiment groups will be less likely to include |
| // users from other regions unnecessarily (since a client will be included in |
| // the experiment if the provider feature flag is checked). |
| return secure_dns::SelectEnabledProviders(secure_dns::ProvidersForCountry( |
| net::DohProviderEntry::GetList(), country_codes::GetCurrentCountryID())); |
| } |
| |
| // Runs a DNS probe according to the configuration in |overrides|, |
| // asynchronously sets |success| to indicate the result, and signals |
| // |waiter| when the probe has completed. Must run on the UI thread. |
| void RunProbe(base::WaitableEvent* waiter, |
| bool* success, |
| const std::string& doh_config) { |
| std::optional<net::DnsOverHttpsConfig> parsed = |
| net::DnsOverHttpsConfig::FromString(doh_config); |
| DCHECK(parsed.has_value()); // `doh_config` must be valid. |
| auto* manager = g_browser_process->system_network_context_manager(); |
| auto runner = secure_dns::MakeProbeRunner( |
| std::move(*parsed), |
| base::BindRepeating(&SystemNetworkContextManager::GetContext, |
| base::Unretained(manager))); |
| auto* const runner_ptr = runner.get(); |
| runner_ptr->RunProbe(base::BindOnce( |
| [](base::WaitableEvent* waiter, bool* success, |
| std::unique_ptr<DnsProbeRunner> runner) { |
| *success = runner->result() == DnsProbeRunner::CORRECT; |
| waiter->Signal(); |
| }, |
| waiter, success, std::move(runner))); |
| } |
| |
| } // namespace |
| |
| static jint JNI_SecureDnsBridge_GetMode(JNIEnv* env) { |
| return static_cast<int>( |
| SystemNetworkContextManager::GetStubResolverConfigReader() |
| ->GetSecureDnsConfiguration( |
| true /* force_check_parental_controls_for_automatic_mode */) |
| .mode()); |
| } |
| |
| static void JNI_SecureDnsBridge_SetMode(JNIEnv* env, jint mode) { |
| PrefService* local_state = g_browser_process->local_state(); |
| local_state->SetString( |
| prefs::kDnsOverHttpsMode, |
| SecureDnsConfig::ModeToString(static_cast<net::SecureDnsMode>(mode))); |
| } |
| |
| static jboolean JNI_SecureDnsBridge_IsModeManaged(JNIEnv* env) { |
| PrefService* local_state = g_browser_process->local_state(); |
| return local_state->IsManagedPreference(prefs::kDnsOverHttpsMode); |
| } |
| |
| static ScopedJavaLocalRef<jobjectArray> JNI_SecureDnsBridge_GetProviders( |
| JNIEnv* env) { |
| net::DohProviderEntry::List providers = GetFilteredProviders(); |
| std::vector<std::vector<std::u16string>> ret; |
| ret.reserve(providers.size()); |
| std::ranges::transform( |
| providers, std::back_inserter(ret), |
| [](const net::DohProviderEntry* entry) -> std::vector<std::u16string> { |
| net::DnsOverHttpsConfig config({entry->doh_server_config}); |
| return {base::UTF8ToUTF16(entry->ui_name), |
| base::UTF8ToUTF16(config.ToString()), |
| base::UTF8ToUTF16(entry->privacy_policy)}; |
| }); |
| return base::android::ToJavaArrayOfStringArray(env, ret); |
| } |
| |
| static ScopedJavaLocalRef<jstring> JNI_SecureDnsBridge_GetConfig(JNIEnv* env) { |
| PrefService* local_state = g_browser_process->local_state(); |
| return base::android::ConvertUTF8ToJavaString( |
| env, local_state->GetString(prefs::kDnsOverHttpsTemplates)); |
| } |
| |
| static jboolean JNI_SecureDnsBridge_SetConfig( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& jconfig) { |
| PrefService* local_state = g_browser_process->local_state(); |
| std::string config = base::android::ConvertJavaStringToUTF8(jconfig); |
| if (config.empty()) { |
| local_state->ClearPref(prefs::kDnsOverHttpsTemplates); |
| return true; |
| } |
| |
| if (net::DnsOverHttpsConfig::FromString(config).has_value()) { |
| local_state->SetString(prefs::kDnsOverHttpsTemplates, config); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static jint JNI_SecureDnsBridge_GetManagementMode(JNIEnv* env) { |
| return static_cast<int>( |
| SystemNetworkContextManager::GetStubResolverConfigReader() |
| ->GetSecureDnsConfiguration( |
| true /* force_check_parental_controls_for_automatic_mode */) |
| .management_mode()); |
| } |
| |
| static void JNI_SecureDnsBridge_UpdateValidationHistogram(JNIEnv* env, |
| jboolean valid) { |
| secure_dns::UpdateValidationHistogram(valid); |
| } |
| |
| static jboolean JNI_SecureDnsBridge_ProbeConfig( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& doh_config) { |
| // Android recommends converting async functions to blocking when using JNI: |
| // https://developer.android.com/training/articles/perf-jni. |
| // This function converts the DnsProbeRunner, which can only be created and |
| // used on the UI thread, into a blocking function that can be called from an |
| // auxiliary Java thread. |
| // TODO: Use std::future if allowed in the future. |
| base::WaitableEvent waiter; |
| bool success; |
| bool posted = content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(RunProbe, &waiter, &success, |
| base::android::ConvertJavaStringToUTF8(doh_config))); |
| DCHECK(posted); |
| waiter.Wait(); |
| |
| secure_dns::UpdateProbeHistogram(success); |
| return success; |
| } |