diff --git a/android/BUILD.gn b/android/BUILD.gn index 14050578f92..f0b61e8386f 100644 --- a/android/BUILD.gn +++ b/android/BUILD.gn @@ -3,6 +3,7 @@ import("//build/config/android/rules.gni") declare_args() { brave_android_developer_options_code = "" + brave_safebrowsing_api_key = "" } java_cpp_enum("brave_android_java_enums_srcjar") { @@ -17,5 +18,6 @@ java_cpp_template("brave_config_java") { defines = [ "BRAVE_ANDROID_DEVELOPER_OPTIONS_CODE=\"$brave_android_developer_options_code\"", "BRAVE_ANDROID_P3A_ENABLED=$brave_p3a_enabled", + "BRAVE_SAFEBROWSING_API_KEY=\"$brave_safebrowsing_api_key\"", ] } diff --git a/android/brave_java_sources.gni b/android/brave_java_sources.gni index de60d8c2d86..ef1aa285cb3 100644 --- a/android/brave_java_sources.gni +++ b/android/brave_java_sources.gni @@ -277,6 +277,7 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/rewards/BraveRewardsTipFailureFragment.java", "../../brave/android/java/org/chromium/chrome/browser/rewards/BraveRewardsTippingPanelFragment.java", "../../brave/android/java/org/chromium/chrome/browser/safe_browsing/settings/BraveStandardProtectionSettingsFragment.java", + "../../brave/android/java/org/chromium/chrome/browser/safe_browsing/settings/NoGooglePlayServicesDialog.java", "../../brave/android/java/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderActivity.java", "../../brave/android/java/org/chromium/chrome/browser/set_default_browser/BraveSetDefaultBrowserUtils.java", "../../brave/android/java/org/chromium/chrome/browser/set_default_browser/OnBraveSetDefaultBrowserListener.java", diff --git a/android/java/org/chromium/base/BraveFeatureList.java b/android/java/org/chromium/base/BraveFeatureList.java index cabbfa4c7ac..6e4275585f2 100644 --- a/android/java/org/chromium/base/BraveFeatureList.java +++ b/android/java/org/chromium/base/BraveFeatureList.java @@ -16,6 +16,7 @@ public abstract class BraveFeatureList { public static final String BRAVE_BACKGROUND_VIDEO_PLAYBACK = "BraveBackgroundVideoPlayback"; public static final String BRAVE_BACKGROUND_VIDEO_PLAYBACK_INTERNAL = "brave-background-video-playback"; + public static final String BRAVE_ANDROID_SAFE_BROWSING = "BraveAndroidSafeBrowsing"; public static final String BRAVE_VPN_LINK_SUBSCRIPTION_ANDROID_UI = "BraveVPNLinkSubscriptionAndroidUI"; } diff --git a/android/java/org/chromium/chrome/browser/app/BraveActivity.java b/android/java/org/chromium/chrome/browser/app/BraveActivity.java index 047a73ab0aa..49df0d51cab 100644 --- a/android/java/org/chromium/chrome/browser/app/BraveActivity.java +++ b/android/java/org/chromium/chrome/browser/app/BraveActivity.java @@ -80,6 +80,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ApplicationLifetime; import org.chromium.chrome.browser.BraveAdFreeCalloutDialogFragment; import org.chromium.chrome.browser.BraveAdaptiveCaptchaUtils; +import org.chromium.chrome.browser.BraveConfig; import org.chromium.chrome.browser.BraveFeatureUtil; import org.chromium.chrome.browser.BraveHelper; import org.chromium.chrome.browser.BraveRelaunchUtils; @@ -146,6 +147,8 @@ import org.chromium.chrome.browser.privacy.settings.BravePrivacySettings; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.rate.RateDialogFragment; import org.chromium.chrome.browser.rate.RateUtils; +import org.chromium.chrome.browser.safe_browsing.SafeBrowsingBridge; +import org.chromium.chrome.browser.safe_browsing.SafeBrowsingState; import org.chromium.chrome.browser.set_default_browser.BraveSetDefaultBrowserUtils; import org.chromium.chrome.browser.set_default_browser.OnBraveSetDefaultBrowserListener; import org.chromium.chrome.browser.settings.BraveNewsPreferences; @@ -182,6 +185,8 @@ import org.chromium.chrome.browser.vpn.wireguard.WireguardConfigUtils; import org.chromium.components.browser_ui.settings.SettingsLauncher; import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.components.embedder_support.util.UrlUtilities; +import org.chromium.components.safe_browsing.BraveSafeBrowsingApiHandler; +import org.chromium.components.safe_browsing.SafeBrowsingApiBridge; import org.chromium.components.search_engines.TemplateUrl; import org.chromium.components.user_prefs.UserPrefs; import org.chromium.content_public.browser.WebContents; @@ -204,7 +209,8 @@ import java.util.concurrent.CopyOnWriteArrayList; @JNINamespace("chrome::android") public abstract class BraveActivity extends ChromeActivity implements BrowsingDataBridge.OnClearBrowsingDataListener, BraveVpnObserver, - OnBraveSetDefaultBrowserListener, ConnectionErrorHandler, PrefObserver { + OnBraveSetDefaultBrowserListener, ConnectionErrorHandler, PrefObserver, + BraveSafeBrowsingApiHandler.BraveSafeBrowsingApiHandlerDelegate { public static final String ADD_FUNDS_URL = "brave://rewards/#add-funds"; public static final String BRAVE_REWARDS_SETTINGS_URL = "brave://rewards/"; public static final String BRAVE_REWARDS_SETTINGS_WALLET_VERIFICATION_URL = @@ -235,6 +241,8 @@ public abstract class BraveActivity extends C private static final List yandexRegions = Arrays.asList("AM", "AZ", "BY", "KG", "KZ", "MD", "RU", "TJ", "TM", "UZ"); + private BraveSafeBrowsingApiHandler mBraveSafeBrowsingApiHandler; + private String mPurchaseToken = ""; private String mProductId = ""; private boolean mIsVerification; @@ -293,6 +301,15 @@ public abstract class BraveActivity extends C } updateWalletBadgeVisibility(); } + boolean safeBrowsingFlagEnabled = + ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_ANDROID_SAFE_BROWSING); + boolean safeBrowsingPrefEnabled = + SafeBrowsingBridge.getSafeBrowsingState() != SafeBrowsingState.NO_SAFE_BROWSING; + if (safeBrowsingFlagEnabled && safeBrowsingPrefEnabled) { + mBraveSafeBrowsingApiHandler.initSafeBrowsing(); + } else if (!safeBrowsingFlagEnabled && safeBrowsingPrefEnabled) { + SafeBrowsingBridge.setSafeBrowsingState(SafeBrowsingState.NO_SAFE_BROWSING); + } } @Override @@ -765,6 +782,18 @@ public abstract class BraveActivity extends C PostTask.postTask( TaskTraits.BEST_EFFORT_MAY_BLOCK, () -> { BraveStatsUtil.removeShareStatsFile(); }); + if (mBraveSafeBrowsingApiHandler == null) { + mBraveSafeBrowsingApiHandler = + new BraveSafeBrowsingApiHandler(this, BraveConfig.SAFEBROWSING_API_KEY, this); + SafeBrowsingApiBridge.setHandler(mBraveSafeBrowsingApiHandler); + SafeBrowsingApiBridge.ensureInitialized(); + } + } + + @Override + public void onPause() { + super.onPause(); + mBraveSafeBrowsingApiHandler.shutdownSafeBrowsing(); } @Override @@ -788,6 +817,11 @@ public abstract class BraveActivity extends C maybeSolveAdaptiveCaptcha(); } + @Override + public void turnSafeBrowsingOff() { + SafeBrowsingBridge.setSafeBrowsingState(SafeBrowsingState.NO_SAFE_BROWSING); + } + public void maybeSolveAdaptiveCaptcha() { String captchaID = UserPrefs.get(Profile.getLastUsedRegularProfile()) .getString(BravePref.SCHEDULED_CAPTCHA_ID); diff --git a/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java b/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java index 3d3b238e233..03d1954bac9 100644 --- a/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java +++ b/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java @@ -13,11 +13,13 @@ import androidx.preference.ListPreference; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; +import org.chromium.base.BraveFeatureList; import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.brave_shields.mojom.CookieListOptInPageAndroidHandler; import org.chromium.chrome.R; import org.chromium.chrome.browser.BraveConfig; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.metrics.UmaSessionStats; import org.chromium.chrome.browser.preferences.BravePref; import org.chromium.chrome.browser.preferences.BravePrefServiceBridge; @@ -27,6 +29,7 @@ import org.chromium.chrome.browser.preferences.website.BraveShieldsContentSettin import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl; import org.chromium.chrome.browser.privacy.settings.PrivacySettings; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.safe_browsing.settings.NoGooglePlayServicesDialog; import org.chromium.chrome.browser.settings.BraveDialogPreference; import org.chromium.chrome.browser.settings.BravePreferenceDialogFragment; import org.chromium.chrome.browser.settings.BraveWebrtcPolicyPreference; @@ -38,6 +41,7 @@ import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; import org.chromium.components.browser_ui.settings.SettingsUtils; import org.chromium.components.prefs.PrefService; import org.chromium.components.user_prefs.UserPrefs; +import org.chromium.gms.ChromiumPlayServicesAvailability; import org.chromium.mojo.bindings.ConnectionErrorHandler; import org.chromium.mojo.system.MojoException; @@ -276,7 +280,23 @@ public class BravePrivacySettings extends PrivacySettings implements ConnectionE removePreferenceIfPresent(PREF_SYNC_AND_SERVICES_LINK); removePreferenceIfPresent(PREF_NETWORK_PREDICTIONS); removePreferenceIfPresent(PREF_PRIVACY_SANDBOX); - removePreferenceIfPresent(PREF_SAFE_BROWSING); + if (!ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_ANDROID_SAFE_BROWSING)) { + removePreferenceIfPresent(PREF_SAFE_BROWSING); + } else { + Preference preference = getPreferenceScreen().findPreference(PREF_SAFE_BROWSING); + if (preference != null) { + preference.setOnPreferenceClickListener((pref) -> { + if (!ChromiumPlayServicesAvailability.isGooglePlayServicesAvailable( + getActivity())) { + NoGooglePlayServicesDialog.create(getContext()).show(); + // Don't show the menu if Google Play Services are not available + return true; + } + + return false; + }); + } + } if (mCookieListOptInPageAndroidHandler != null) { mCookieListOptInPageAndroidHandler.shouldShowDialog(shouldShowDialog -> { diff --git a/android/java/org/chromium/chrome/browser/safe_browsing/settings/NoGooglePlayServicesDialog.java b/android/java/org/chromium/chrome/browser/safe_browsing/settings/NoGooglePlayServicesDialog.java new file mode 100644 index 00000000000..076420e584f --- /dev/null +++ b/android/java/org/chromium/chrome/browser/safe_browsing/settings/NoGooglePlayServicesDialog.java @@ -0,0 +1,66 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +package org.chromium.chrome.browser.safe_browsing.settings; + +import android.content.Context; +import android.content.res.Resources; + +import org.chromium.components.browser_ui.modaldialog.AppModalPresenter; +import org.chromium.ui.modaldialog.DialogDismissalCause; +import org.chromium.ui.modaldialog.ModalDialogManager; +import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType; +import org.chromium.ui.modaldialog.ModalDialogProperties; +import org.chromium.ui.modaldialog.ModalDialogProperties.Controller; +import org.chromium.ui.modelutil.PropertyModel; + +/** + * Dialog to notify that the user is not protected by Safe Browsing as Google Play Services are not + * available. + */ +public class NoGooglePlayServicesDialog { + private Context mContext; + private ModalDialogManager mManager; + private PropertyModel mModel; + + public static NoGooglePlayServicesDialog create(Context context) { + return new NoGooglePlayServicesDialog(context); + } + + private NoGooglePlayServicesDialog(Context context) { + mContext = context; + } + + /** Show this dialog in the context of its enclosing activity. */ + public void show() { + Resources resources = mContext.getResources(); + PropertyModel.Builder builder = + new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS) + .with(ModalDialogProperties.CONTROLLER, makeController()) + .with(ModalDialogProperties.TITLE, resources, + R.string.safe_browsing_no_google_play_services_dialog_title) + .with(ModalDialogProperties.MESSAGE_PARAGRAPH_1, + resources.getString( + R.string.safe_browsing_no_google_play_services_dialog_message)) + .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, R.string.ok); + mModel = builder.build(); + mManager = new ModalDialogManager(new AppModalPresenter(mContext), ModalDialogType.APP); + mManager.showDialog(mModel, ModalDialogType.APP); + } + + private Controller makeController() { + return new ModalDialogProperties.Controller() { + @Override + public void onClick(PropertyModel model, int buttonType) { + mManager.dismissDialog(mModel, DialogDismissalCause.POSITIVE_BUTTON_CLICKED); + } + + @Override + public void onDismiss(PropertyModel model, int dismissalCause) { + mManager.destroy(); + } + }; + } +} diff --git a/browser/about_flags.cc b/browser/about_flags.cc index 8bb4cd56b29..968a2c708b3 100644 --- a/browser/about_flags.cc +++ b/browser/about_flags.cc @@ -58,6 +58,7 @@ #if BUILDFLAG(IS_ANDROID) #include "brave/browser/android/preferences/features.h" +#include "brave/browser/android/safe_browsing/features.h" #endif using brave_shields::features::kBraveAdblockCnameUncloaking; @@ -83,6 +84,7 @@ using ntp_background_images::features::kBraveNTPSuperReferralWallpaper; #if BUILDFLAG(IS_ANDROID) using preferences::features::kBraveBackgroundVideoPlayback; +using safe_browsing::features::kBraveAndroidSafeBrowsing; #endif namespace flag_descriptions { @@ -400,6 +402,10 @@ constexpr char kBraveBackgroundVideoPlaybackDescription[] = "Enables play audio from video in background when tab is not active or " "device screen is turned off. Try to switch to desktop mode if this " "feature is not working."; +constexpr char kBraveAndroidSafeBrowsingName[] = "Safe Browsing"; +constexpr char kBraveAndroidSafeBrowsingDescription[] = + "Enables Google Safe Browsing for determining whether a URL has been " + "marked as a known threat."; #endif } // namespace @@ -563,8 +569,15 @@ constexpr char kBraveBackgroundVideoPlaybackDescription[] = flag_descriptions::kBraveBackgroundVideoPlaybackDescription, \ kOsAndroid, \ FEATURE_VALUE_TYPE(kBraveBackgroundVideoPlayback)}, +#define BRAVE_SAFE_BROWSING_ANDROID \ + {"brave-safe-browsing", \ + flag_descriptions::kBraveAndroidSafeBrowsingName, \ + flag_descriptions::kBraveAndroidSafeBrowsingDescription, \ + kOsAndroid, \ + FEATURE_VALUE_TYPE(kBraveAndroidSafeBrowsing)}, #else #define BRAVE_BACKGROUND_VIDEO_PLAYBACK_ANDROID +#define BRAVE_SAFE_BROWSING_ANDROID #endif // BUILDFLAG(IS_ANDROID) #define BRAVE_ABOUT_FLAGS_FEATURE_ENTRIES \ @@ -727,4 +740,5 @@ constexpr char kBraveBackgroundVideoPlaybackDescription[] = BRAVE_FEDERATED_FEATURE_ENTRIES \ PLAYLIST_FEATURE_ENTRIES \ BRAVE_VERTICAL_TABS_FEATURE_ENTRY \ - BRAVE_BACKGROUND_VIDEO_PLAYBACK_ANDROID + BRAVE_BACKGROUND_VIDEO_PLAYBACK_ANDROID \ + BRAVE_SAFE_BROWSING_ANDROID diff --git a/browser/android/BUILD.gn b/browser/android/BUILD.gn index 7b422426627..b81e8156420 100644 --- a/browser/android/BUILD.gn +++ b/browser/android/BUILD.gn @@ -17,6 +17,7 @@ source_set("android_browser_process") { deps = [ "//base", "//brave/browser/android/preferences", + "//brave/browser/android/safe_browsing", "//brave/browser/brave_ads/android:jni_headers", "//brave/build/android:jni_headers", "//brave/components/brave_ads/browser", diff --git a/browser/android/safe_browsing/BUILD.gn b/browser/android/safe_browsing/BUILD.gn new file mode 100644 index 00000000000..79d39164a37 --- /dev/null +++ b/browser/android/safe_browsing/BUILD.gn @@ -0,0 +1,15 @@ +# 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 https://mozilla.org/MPL/2.0/. + +import("//build/config/android/rules.gni") + +source_set("safe_browsing") { + sources = [ + "features.cc", + "features.h", + ] + + deps = [ "//base" ] +} diff --git a/browser/android/safe_browsing/features.cc b/browser/android/safe_browsing/features.cc new file mode 100644 index 00000000000..30a37a2a915 --- /dev/null +++ b/browser/android/safe_browsing/features.cc @@ -0,0 +1,18 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/android/safe_browsing/features.h" + +#include "base/feature_list.h" + +namespace safe_browsing { +namespace features { + +BASE_FEATURE(kBraveAndroidSafeBrowsing, + "BraveAndroidSafeBrowsing", + base::FEATURE_DISABLED_BY_DEFAULT); + +} // namespace features +} // namespace safe_browsing diff --git a/browser/android/safe_browsing/features.h b/browser/android/safe_browsing/features.h new file mode 100644 index 00000000000..95424047d57 --- /dev/null +++ b/browser/android/safe_browsing/features.h @@ -0,0 +1,19 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_ANDROID_SAFE_BROWSING_FEATURES_H_ +#define BRAVE_BROWSER_ANDROID_SAFE_BROWSING_FEATURES_H_ + +#include "base/feature_list.h" + +namespace safe_browsing { +namespace features { + +BASE_DECLARE_FEATURE(kBraveAndroidSafeBrowsing); + +} // namespace features +} // namespace safe_browsing + +#endif // BRAVE_BROWSER_ANDROID_SAFE_BROWSING_FEATURES_H_ diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd index 26e5954a2a7..1de998654e6 100644 --- a/browser/ui/android/strings/android_brave_strings.grd +++ b/browser/ui/android/strings/android_brave_strings.grd @@ -3309,6 +3309,12 @@ If you don't accept this request, VPN will not reconnect and your internet conne Your Brave VPN subscription can protect up to 5 devices, across Android, iOS, and desktop. Just link your Google Play subscription to your Brave account. + + Cannot enable Safe Browsing + + + Safe Browsing has been disabled on your device. To enable it, ensure that Google Play Services is installed and that it's up to date. + diff --git a/build/android/config.gni b/build/android/config.gni index 720fbd4eda8..bbe19416eb9 100644 --- a/build/android/config.gni +++ b/build/android/config.gni @@ -25,6 +25,7 @@ brave_chrome_java_deps = [ "//brave/components/brave_today/common:mojom_java", "//brave/components/brave_wallet/common:mojom_java", "//brave/components/browser_ui/site_settings/android:java", + "//brave/components/safe_browsing/android:brave_safe_browsing_java", "//brave/components/variations/android:java", "//brave/third_party/android_deps:com_airbnb_android_java", "//brave/third_party/android_deps:com_android_billingclient_java", diff --git a/build/android/java/templates/BraveConfig.template b/build/android/java/templates/BraveConfig.template index fc183624b43..990eb26a604 100644 --- a/build/android/java/templates/BraveConfig.template +++ b/build/android/java/templates/BraveConfig.template @@ -16,8 +16,14 @@ public class BraveConfig { #endif #if defined(BRAVE_ANDROID_P3A_ENABLED) -public static final boolean P3A_ENABLED = BRAVE_ANDROID_P3A_ENABLED; + public static final boolean P3A_ENABLED = BRAVE_ANDROID_P3A_ENABLED; #else -public static final boolean P3A_ENABLED = false; + public static final boolean P3A_ENABLED = false; +#endif + +#if defined(BRAVE_SAFEBROWSING_API_KEY) + public static final String SAFEBROWSING_API_KEY = BRAVE_SAFEBROWSING_API_KEY; +#else + public static final String SAFEBROWSING_API_KEY = ""; #endif } diff --git a/build/commands/lib/config.js b/build/commands/lib/config.js index e41a8deede0..ba5bfc9b39c 100644 --- a/build/commands/lib/config.js +++ b/build/commands/lib/config.js @@ -170,6 +170,7 @@ const Config = function () { this.extraGnArgs = {} this.extraGnGenOpts = getNPMConfig(['brave_extra_gn_gen_opts']) || '' this.extraNinjaOpts = [] + this.braveAndroidSafeBrowsingApiKey = getNPMConfig(['brave_safebrowsing_api_key']) || '' this.braveSafetyNetApiKey = getNPMConfig(['brave_safetynet_api_key']) || '' this.braveAndroidDeveloperOptionsCode = getNPMConfig(['brave_android_developer_options_code']) || '' this.braveAndroidKeystorePath = getNPMConfig(['brave_android_keystore_path']) @@ -468,6 +469,7 @@ Config.prototype.buildArgs = function () { args.brave_android_developer_options_code = this.braveAndroidDeveloperOptionsCode args.brave_safetynet_api_key = this.braveSafetyNetApiKey + args.brave_safebrowsing_api_key = this.braveAndroidSafeBrowsingApiKey args.enable_widevine = false args.safe_browsing_mode = 2 @@ -718,6 +720,10 @@ Config.prototype.update = function (options) { this.braveGoogleApiKey = options.brave_google_api_key } + if (options.brave_safebrowsing_api_key) { + this.braveAndroidSafeBrowsingApiKey = options.brave_safebrowsing_api_key + } + if (options.brave_safetynet_api_key) { this.braveSafetyNetApiKey = options.brave_safetynet_api_key } diff --git a/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc b/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc index baccd51df45..94966012bf5 100644 --- a/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chromium_src/chrome/browser/flags/android/chrome_feature_list.cc @@ -5,6 +5,7 @@ #include "base/feature_override.h" #include "brave/browser/android/preferences/features.h" +#include "brave/browser/android/safe_browsing/features.h" #include "brave/components/brave_rewards/common/features.h" #include "brave/components/brave_search_conversion/features.h" #include "brave/components/brave_today/common/features.h" @@ -17,10 +18,11 @@ &brave_rewards::features::kBraveRewards, \ &brave_search_conversion::features::kOmniboxBanner, \ &brave_today::features::kBraveNewsFeature, \ - &brave_vpn::features::kBraveVPNLinkSubscriptionAndroidUI, \ + &brave_vpn::features::kBraveVPNLinkSubscriptionAndroidUI,\ &brave_wallet::features::kNativeBraveWalletFeature, \ &brave_wallet::features::kBraveWalletSolanaFeature, \ - &preferences::features::kBraveBackgroundVideoPlayback + &preferences::features::kBraveBackgroundVideoPlayback, \ + &safe_browsing::features::kBraveAndroidSafeBrowsing // clang-format on #include "src/chrome/browser/flags/android/chrome_feature_list.cc" diff --git a/components/safe_browsing/android/BUILD.gn b/components/safe_browsing/android/BUILD.gn new file mode 100644 index 00000000000..f53c5568255 --- /dev/null +++ b/components/safe_browsing/android/BUILD.gn @@ -0,0 +1,19 @@ +# 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 https://mozilla.org/MPL/2.0/. + +import("//build/config/android/config.gni") +import("//build/config/android/rules.gni") + +android_library("brave_safe_browsing_java") { + deps = [ + "//base:base_java", + "//brave/third_party/android_deps:google_play_services_safetynet_java", + "//build/android:build_java", + "//components/safe_browsing/android:safe_browsing_java", + "//third_party/android_deps:google_play_services_basement_java", + "//third_party/android_deps:google_play_services_tasks_java", + ] + sources = [ "java/src/org/chromium/components/safe_browsing/BraveSafeBrowsingApiHandler.java" ] +} diff --git a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/BraveSafeBrowsingApiHandler.java b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/BraveSafeBrowsingApiHandler.java new file mode 100644 index 00000000000..7e19d2b4c9b --- /dev/null +++ b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/BraveSafeBrowsingApiHandler.java @@ -0,0 +1,162 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +package org.chromium.components.safe_browsing; + +import android.app.Activity; +import android.content.pm.ApplicationInfo; + +import com.google.android.gms.common.api.ApiException; +import com.google.android.gms.common.api.CommonStatusCodes; +import com.google.android.gms.safetynet.SafeBrowsingThreat; +import com.google.android.gms.safetynet.SafetyNet; +import com.google.android.gms.safetynet.SafetyNetApi; +import com.google.android.gms.safetynet.SafetyNetStatusCodes; +import com.google.android.gms.tasks.OnSuccessListener; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import org.chromium.base.ContextUtils; +import org.chromium.base.Log; + +public class BraveSafeBrowsingApiHandler implements SafeBrowsingApiHandler { + private static final long DEFAULT_CHECK_DELTA = 10; + private static final String SAFE_METADATA = "{}"; + private static final String TAG = "BraveSafeBrowsingApiHandler"; + + public interface BraveSafeBrowsingApiHandlerDelegate { + default void turnSafeBrowsingOff() {} + } + + private Observer mObserver; + private Activity mActivity; + private String mApiKey; + private boolean mInitialized; + private int mTriesCount; + private BraveSafeBrowsingApiHandlerDelegate mBraveSafeBrowsingApiHandlerDelegate; + + public BraveSafeBrowsingApiHandler(Activity activity, String apiKey, + BraveSafeBrowsingApiHandlerDelegate braveSafeBrowsingApiHandlerDelegate) { + mActivity = activity; + mApiKey = apiKey; + mTriesCount = 0; + mBraveSafeBrowsingApiHandlerDelegate = braveSafeBrowsingApiHandlerDelegate; + assert mBraveSafeBrowsingApiHandlerDelegate + != null : "BraveSafeBrowsingApiHandlerDelegate has to be initialized"; + } + + @Override + public boolean init(Observer observer) { + mObserver = observer; + return true; + } + + @Override + public void startUriLookup(final long callbackId, String uri, int[] threatsOfInterest) { + if (mActivity == null) { + return; + } + mTriesCount++; + if (!mInitialized) { + initSafeBrowsing(); + } + + SafetyNet.getClient(ContextUtils.getApplicationContext()) + .lookupUri(uri, mApiKey, threatsOfInterest) + .addOnSuccessListener(mActivity, + sbResponse -> { + mTriesCount = 0; + try { + String metadata = SAFE_METADATA; + if (!sbResponse.getDetectedThreats().isEmpty()) { + JSONArray jsonArray = new JSONArray(); + for (int i = 0; i < sbResponse.getDetectedThreats().size(); + i++) { + JSONObject jsonObj = new JSONObject(); + jsonObj.put("threat_type", + String.valueOf(sbResponse.getDetectedThreats() + .get(i) + .getThreatType())); + jsonArray.put(jsonObj); + } + JSONObject finalObj = new JSONObject(); + finalObj.put("matches", jsonArray); + metadata = finalObj.toString(); + } + if (mObserver != null) { + mObserver.onUrlCheckDone(callbackId, SafeBrowsingResult.SUCCESS, + metadata, DEFAULT_CHECK_DELTA); + } + } catch (JSONException e) { + } + }) + .addOnFailureListener(mActivity, e -> { + // An error occurred while communicating with the service. + if (e instanceof ApiException) { + // An error with the Google Play Services API contains some + // additional details. + ApiException apiException = (ApiException) e; + if (isDebuggable()) { + Log.d(TAG, + "Error: " + + CommonStatusCodes.getStatusCodeString( + apiException.getStatusCode()) + + ", code: " + apiException.getStatusCode()); + } + if (apiException.getStatusCode() == CommonStatusCodes.API_NOT_CONNECTED) { + // That means that device doesn't have Google Play Services API. + // Delegate is used to turn off safe browsing option as every request is + // delayed when it's turned on and not working + mBraveSafeBrowsingApiHandlerDelegate.turnSafeBrowsingOff(); + } + + // Note: If the status code, apiException.getStatusCode(), + // is SafetyNetStatusCodes.SAFE_BROWSING_API_NOT_INITIALIZED, + // you need to call initSafeBrowsing(). It means either you + // haven't called initSafeBrowsing() before or that it needs + // to be called again due to an internal error. + if (mTriesCount <= 1 + && apiException.getStatusCode() + == SafetyNetStatusCodes.SAFE_BROWSING_API_NOT_INITIALIZED) { + initSafeBrowsing(); + startUriLookup(callbackId, uri, threatsOfInterest); + } + } else { + // A different, unknown type of error occurred. + if (isDebuggable()) { + Log.d(TAG, "Error: " + e.getMessage()); + } + } + mTriesCount = 0; + }); + } + + @Override + public boolean startAllowlistLookup(final String uri, int threatType) { + return false; + } + + public void initSafeBrowsing() { + SafetyNet.getClient(mActivity).initSafeBrowsing(); + mInitialized = true; + } + + public void shutdownSafeBrowsing() { + if (!mInitialized) { + return; + } + SafetyNet.getClient(ContextUtils.getApplicationContext()).shutdownSafeBrowsing(); + } + + private boolean isDebuggable() { + if (mActivity == null) { + return false; + } + + return 0 != (mActivity.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE); + } +}