From 025f59319f538f0a64eb0cf55624dd0116d9a4d2 Mon Sep 17 00:00:00 2001 From: AlexeyBarabash Date: Thu, 14 Sep 2023 23:47:35 +0300 Subject: [PATCH] Connect/disconnect crypto accounts from ConnectAccountFragment through permissions --- .../dapps/ConnectAccountFragment.java | 80 ++++++++++++--- .../BraveDappPermissionPromptDialog.java | 42 ++++++-- browser/brave_wallet/android/sources.gni | 1 + ...ission_prompt_dialog_controller_android.cc | 5 +- ...mission_prompt_dialog_controller_android.h | 6 +- .../brave_wallet_permission_prompt_android.cc | 3 +- .../brave_wallet_permission_prompt_android.h | 3 +- .../permissions/connect_account_android.cc | 97 +++++++++++++++++++ build/android/BUILD.gn | 1 + 9 files changed, 211 insertions(+), 27 deletions(-) create mode 100644 browser/permissions/connect_account_android.cc diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/ConnectAccountFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/ConnectAccountFragment.java index f48dd12f418d..d0fe0aa437ad 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/ConnectAccountFragment.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/ConnectAccountFragment.java @@ -20,10 +20,15 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment; +import org.chromium.base.Callback; import org.chromium.base.Log; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.NativeMethods; import org.chromium.brave_wallet.mojom.AccountInfo; import org.chromium.brave_wallet.mojom.CoinType; +import org.chromium.brave_wallet.mojom.PermissionLifetimeOption; import org.chromium.chrome.R; +import org.chromium.chrome.browser.BraveRewardsHelper; import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.app.BraveActivity; import org.chromium.chrome.browser.app.domain.WalletModel; @@ -32,21 +37,26 @@ import org.chromium.chrome.browser.crypto_wallet.util.AccountsPermissionsHelper; import org.chromium.chrome.browser.crypto_wallet.util.Utils; import org.chromium.chrome.browser.crypto_wallet.util.WalletUtils; +import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.ui.favicon.FaviconHelper; import org.chromium.chrome.browser.ui.favicon.FaviconHelper.DefaultFaviconHelper; import org.chromium.chrome.browser.ui.favicon.FaviconHelper.FaviconImageCallback; +import org.chromium.content_public.browser.WebContents; import org.chromium.url.GURL; import java.util.HashSet; import java.util.Iterator; +/** + * Fragment used to connect Dapps to the crypto account + */ public class ConnectAccountFragment extends BaseDAppsFragment implements BravePermissionAccountsListAdapter.BravePermissionDelegate { private static final String TAG = "ConnectAccount"; private TextView mWebSite; private TextView mAccountsConnected; - private TextView mbtNewAccount; + private TextView mButtonNewAccount; private ImageView mFavicon; private AccountInfo[] mAccountInfos; private HashSet mAccountsWithPermissions; @@ -99,8 +109,8 @@ public View onCreateView( View view = inflater.inflate(R.layout.fragment_connect_account, container, false); mWebSite = view.findViewById(R.id.fragment_connect_account_website); mAccountsConnected = view.findViewById(R.id.fragment_connect_account_accounts_connected); - mbtNewAccount = view.findViewById(R.id.fragment_connect_account_new_account_id); - mbtNewAccount.setOnClickListener(new View.OnClickListener() { + mButtonNewAccount = view.findViewById(R.id.fragment_connect_account_new_account_id); + mButtonNewAccount.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { BottomSheetDialogFragment sheetDialogFragment = @@ -175,17 +185,56 @@ public AccountInfo getSelectedAccount() { return mSelectedAccount; } + /** + * Data passed from ConnectAccountFragment to BraveDappPermissionPromptDialog for pending + * connect account requests + */ + public static class ConnectAccountPendingData { + ConnectAccountPendingData(String accountAddress, int permissionLifetimeOption) { + this.accountAddress = accountAddress; + this.permissionLifetimeOption = permissionLifetimeOption; + } + public String accountAddress; + public int permissionLifetimeOption; + } + private static ConnectAccountPendingData sConnectAccountPendingData; + + private static void setConnectAccountPendingData( + String accountAddress, int permissionLifetimeOption) { + assert sConnectAccountPendingData == null; + sConnectAccountPendingData = + new ConnectAccountPendingData(accountAddress, permissionLifetimeOption); + } + + public static ConnectAccountPendingData getAndResetConnectAccountPendingData() { + if (sConnectAccountPendingData == null) { + return null; + } + ConnectAccountPendingData connectAccountPendingDataCopy = sConnectAccountPendingData; + sConnectAccountPendingData = null; + return connectAccountPendingDataCopy; + } + @Override public void connectAccount(AccountInfo account) { - getBraveWalletService().addPermission(account.accountId, success -> { - if (!success) { - return; + Tab tab = BraveRewardsHelper.currentActiveChromeTabbedActivityTab(); + if (tab != null) { + if (tab.getWebContents() != null) { + // Static data for BraveDappPermissionPromptDialog.show + setConnectAccountPendingData(account.address, PermissionLifetimeOption.FOREVER); + ConnectAccountFragmentJni.get().connectAccount( + account.address, account.accountId.coin, tab.getWebContents(), success -> { + if (!success) { + return; + } + if (CoinType.SOL != account.accountId.coin) { + getKeyringService().setSelectedAccount( + account.accountId, setSuccess -> {}); + } + updateAccounts(); + }); } - if (CoinType.SOL != account.accountId.coin) { - getKeyringService().setSelectedAccount(account.accountId, setSuccess -> {}); - } - updateAccounts(); - }); + } } @Override @@ -228,4 +277,13 @@ private GURL getCurrentHostHttpAddress() { } return GURL.emptyGURL(); } + @CalledByNative + private static void onConnectAccountDone(Callback callback, Boolean result) { + callback.onResult(result); + } + @NativeMethods + interface Natives { + void connectAccount(String accountAddress, int accountIdCoin, WebContents webContents, + Callback callback); + } } diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/permission/BraveDappPermissionPromptDialog.java b/android/java/org/chromium/chrome/browser/crypto_wallet/permission/BraveDappPermissionPromptDialog.java index 5dc7200870da..f5854c44b397 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/permission/BraveDappPermissionPromptDialog.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/permission/BraveDappPermissionPromptDialog.java @@ -26,11 +26,13 @@ import org.chromium.brave_wallet.mojom.BraveWalletService; import org.chromium.brave_wallet.mojom.CoinType; import org.chromium.brave_wallet.mojom.KeyringService; +import org.chromium.brave_wallet.mojom.PermissionLifetimeOption; import org.chromium.chrome.R; import org.chromium.chrome.browser.app.BraveActivity; import org.chromium.chrome.browser.app.domain.WalletModel; import org.chromium.chrome.browser.crypto_wallet.BraveWalletServiceFactory; import org.chromium.chrome.browser.crypto_wallet.KeyringServiceFactory; +import org.chromium.chrome.browser.crypto_wallet.fragments.dapps.ConnectAccountFragment; import org.chromium.chrome.browser.crypto_wallet.util.Utils; import org.chromium.chrome.browser.crypto_wallet.util.WalletConstants; import org.chromium.components.browser_ui.modaldialog.ModalDialogView; @@ -51,6 +53,9 @@ import java.util.Iterator; import java.util.List; +/** + * Dialog to grant website permissions to use Dapps + */ public class BraveDappPermissionPromptDialog implements ModalDialogProperties.Controller, ImageDownloadCallback, ConnectionErrorHandler { private static final String TAG = "BraveDappPermission"; @@ -113,7 +118,7 @@ void show() { setFavIcon(); mRecyclerView = customView.findViewById(R.id.accounts_list); - InitBraveWalletService(); + initBraveWalletService(); TextView domain = customView.findViewById(R.id.domain); mBraveWalletService.getActiveOrigin( originInfo -> { domain.setText(Utils.geteTldSpanned(originInfo)); }); @@ -131,7 +136,7 @@ void show() { .with(ModalDialogProperties.FILTER_TOUCH_FOR_SECURITY, true) .build(); mModalDialogManager.showDialog(mPropertyModel, ModalDialogType.APP); - InitKeyringService(); + initKeyringService(); try { BraveActivity activity = BraveActivity.getBraveActivity(); activity.dismissWalletPanelOrDialog(); @@ -150,6 +155,11 @@ void show() { initAccounts(); } + @PermissionLifetimeOption.EnumType + int getPermissionLifetimeOption() { + return PermissionLifetimeOption.FOREVER; + } + @NonNull private ViewGroup getPermissionModalViewContainer(View customView) { ViewParent viewParent = customView.getParent(); @@ -162,7 +172,7 @@ private ViewGroup getPermissionModalViewContainer(View customView) { return (ViewGroup) viewParent; } - private void InitBraveWalletService() { + private void initBraveWalletService() { if (mBraveWalletService != null) { return; } @@ -198,6 +208,18 @@ public void onAccountCheckChanged(AccountInfo account, boolean isChecked) { } } mAccountsListAdapter.notifyDataSetChanged(); + + // We are on the flow from ConnectAccountFragment.connectAccount + ConnectAccountFragment.ConnectAccountPendingData capd = + ConnectAccountFragment.getAndResetConnectAccountPendingData(); + if (capd != null) { + final String[] selectedAccounts = {capd.accountAddress}; + BraveDappPermissionPromptDialogJni.get().onPrimaryButtonClicked( + mNativeDialogController, selectedAccounts, + capd.permissionLifetimeOption); + mModalDialogManager.dismissDialog( + mPropertyModel, DialogDismissalCause.POSITIVE_BUTTON_CLICKED); + } }); } @@ -253,7 +275,7 @@ public String[] getSelectedAccounts() { public void onClick(PropertyModel model, @ButtonType int buttonType) { if (buttonType == ButtonType.POSITIVE) { BraveDappPermissionPromptDialogJni.get().onPrimaryButtonClicked( - mNativeDialogController, getSelectedAccounts()); + mNativeDialogController, getSelectedAccounts(), getPermissionLifetimeOption()); mModalDialogManager.dismissDialog( mPropertyModel, DialogDismissalCause.POSITIVE_BUTTON_CLICKED); } else if (buttonType == ButtonType.NEGATIVE) { @@ -266,7 +288,7 @@ public void onClick(PropertyModel model, @ButtonType int buttonType) { @Override public void onDismiss(PropertyModel model, int dismissalCause) { - DisconnectMojoServices(); + disconnectMojoServices(); BraveDappPermissionPromptDialogJni.get().onDialogDismissed(mNativeDialogController); mNativeDialogController = 0; } @@ -276,7 +298,7 @@ private void dismissDialog() { mModalDialogManager.dismissDialog(mPropertyModel, DialogDismissalCause.DISMISSED_BY_NATIVE); } - public void DisconnectMojoServices() { + public void disconnectMojoServices() { mMojoServicesClosed = true; if (mKeyringService != null) { mKeyringService.close(); @@ -295,10 +317,10 @@ public void onConnectionError(MojoException e) { } mKeyringService.close(); mKeyringService = null; - InitKeyringService(); + initKeyringService(); } - protected void InitKeyringService() { + protected void initKeyringService() { if (mKeyringService != null) { return; } @@ -308,8 +330,8 @@ protected void InitKeyringService() { @NativeMethods interface Natives { - void onPrimaryButtonClicked( - long nativeBraveDappPermissionPromptDialogController, String[] accounts); + void onPrimaryButtonClicked(long nativeBraveDappPermissionPromptDialogController, + String[] accounts, int permissionLifetimeOption); void onNegativeButtonClicked(long nativeBraveDappPermissionPromptDialogController); void onDialogDismissed(long nativeBraveDappPermissionPromptDialogController); } diff --git a/browser/brave_wallet/android/sources.gni b/browser/brave_wallet/android/sources.gni index dcf0e68638e4..829f7410a755 100644 --- a/browser/brave_wallet/android/sources.gni +++ b/browser/brave_wallet/android/sources.gni @@ -17,6 +17,7 @@ brave_browser_brave_wallet_android_sources = [ "//brave/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.h", "//brave/browser/permissions/brave_wallet_permission_prompt_android.cc", "//brave/browser/permissions/brave_wallet_permission_prompt_android.h", + "//brave/browser/permissions/connect_account_android.cc", ] brave_browser_brave_wallet_android_deps = [ diff --git a/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.cc b/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.cc index 867557b715f7..9b0c33bbcdb7 100644 --- a/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.cc +++ b/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.cc @@ -52,11 +52,12 @@ void BraveDappPermissionPromptDialogController::ShowDialog() { void BraveDappPermissionPromptDialogController::OnPrimaryButtonClicked( JNIEnv* env, - const base::android::JavaParamRef& accounts) { + const base::android::JavaParamRef& accounts, + int permission_lifetime_option) { std::vector allowedAccounts; base::android::AppendJavaStringArrayToStringVector(env, accounts, &allowedAccounts); - delegate_->ConnectToSite(allowedAccounts); + delegate_->ConnectToSite(allowedAccounts, permission_lifetime_option); } void BraveDappPermissionPromptDialogController::OnNegativeButtonClicked( diff --git a/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.h b/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.h index 3851f9e16535..082330b70a3c 100644 --- a/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.h +++ b/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.h @@ -23,7 +23,8 @@ class BraveDappPermissionPromptDialogController { class Delegate { public: virtual void OnDialogDismissed() = 0; - virtual void ConnectToSite(const std::vector& accounts) = 0; + virtual void ConnectToSite(const std::vector& accounts, + int permission_lifetime_option) = 0; virtual void CancelConnectToSite() = 0; }; @@ -39,7 +40,8 @@ class BraveDappPermissionPromptDialogController { void OnPrimaryButtonClicked( JNIEnv* env, - const base::android::JavaParamRef& accounts); + const base::android::JavaParamRef& accounts, + int permission_lifetime_option); void OnNegativeButtonClicked(JNIEnv* env); void OnDialogDismissed(JNIEnv* env); diff --git a/browser/permissions/brave_wallet_permission_prompt_android.cc b/browser/permissions/brave_wallet_permission_prompt_android.cc index 1ffb5cc78e04..e73af74c9981 100644 --- a/browser/permissions/brave_wallet_permission_prompt_android.cc +++ b/browser/permissions/brave_wallet_permission_prompt_android.cc @@ -27,7 +27,8 @@ BraveWalletPermissionPrompt::BraveWalletPermissionPrompt( BraveWalletPermissionPrompt::~BraveWalletPermissionPrompt() {} void BraveWalletPermissionPrompt::ConnectToSite( - const std::vector& accounts) { + const std::vector& accounts, + int permission_lifetime_option) { has_interacted_with_dialog_ = true; dialog_controller_.reset(); // TODO(SergeyZhukovsky): Use the real option that the user chooses, using diff --git a/browser/permissions/brave_wallet_permission_prompt_android.h b/browser/permissions/brave_wallet_permission_prompt_android.h index e6ebb343d81e..276d8060f8c2 100644 --- a/browser/permissions/brave_wallet_permission_prompt_android.h +++ b/browser/permissions/brave_wallet_permission_prompt_android.h @@ -47,7 +47,8 @@ class BraveWalletPermissionPrompt protected: // BraveDappPermissionPromptDialogController::Delegate: void OnDialogDismissed() override; - void ConnectToSite(const std::vector& accounts) override; + void ConnectToSite(const std::vector& accounts, + int permission_lifetime_option) override; void CancelConnectToSite() override; private: diff --git a/browser/permissions/connect_account_android.cc b/browser/permissions/connect_account_android.cc new file mode 100644 index 000000000000..2b1fff921bb0 --- /dev/null +++ b/browser/permissions/connect_account_android.cc @@ -0,0 +1,97 @@ +/* Copyright (c) 2023 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 "base/android/jni_android.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "brave/build/android/jni_headers/ConnectAccountFragment_jni.h" +#include "brave/components/brave_wallet/browser/permission_utils.h" +#include "brave/components/brave_wallet/common/brave_wallet.mojom-shared.h" +#include "brave/components/permissions/contexts/brave_wallet_permission_context.h" +#include "content/public/browser/web_contents.h" + +#include "base/functional/callback.h" +#include "content/public/browser/render_frame_host.h" + +namespace { + +base::android::ScopedJavaLocalRef GetJavaBoolean( + JNIEnv* env, + const bool& native_bool) { + jclass booleanClass = env->FindClass("java/lang/Boolean"); + jmethodID methodID = env->GetMethodID(booleanClass, "", "(Z)V"); + jobject booleanObject = env->NewObject(booleanClass, methodID, native_bool); + + return base::android::ScopedJavaLocalRef(env, booleanObject); +} + +void PlainCallConnectAccountCallback( + JNIEnv* env, + base::android::ScopedJavaGlobalRef java_callback, + bool value_result) { + Java_ConnectAccountFragment_onConnectAccountDone( + env, java_callback, GetJavaBoolean(env, value_result)); +} + +} // namespace + +static void JNI_ConnectAccountFragment_ConnectAccount( + JNIEnv* env, + const base::android::JavaParamRef& java_account_address, + jint account_id_coin, + const base::android::JavaParamRef& java_web_contents, + const base::android::JavaParamRef& callback) { + base::android::ScopedJavaGlobalRef java_callback; + java_callback.Reset(env, callback); + + content::WebContents* web_contents = + content::WebContents::FromJavaWebContents(java_web_contents); + + if (web_contents == nullptr) { + PlainCallConnectAccountCallback(env, java_callback, false); + return; + } + + std::string account_address = + base::android::ConvertJavaStringToUTF8(java_account_address); + + content::RenderFrameHost* rfh = web_contents->GetFocusedFrame(); + if (rfh == nullptr) { + PlainCallConnectAccountCallback(env, java_callback, false); + return; + } + + brave_wallet::mojom::CoinType coin = + static_cast(account_id_coin); + CHECK(brave_wallet::mojom::IsKnownEnumValue(coin)); + + auto request_type = brave_wallet::CoinTypeToPermissionRequestType(coin); + auto permission = brave_wallet::CoinTypeToPermissionType(coin); + + if (!request_type || !permission) { + PlainCallConnectAccountCallback(env, java_callback, false); + return; + } + + if (permissions::BraveWalletPermissionContext::HasRequestsInProgress( + rfh, *request_type)) { + PlainCallConnectAccountCallback(env, java_callback, false); + return; + } + + permissions::BraveWalletPermissionContext::RequestPermissions( + *permission, rfh, {account_address}, + base::BindOnce( + [](JNIEnv* env, + base::android::ScopedJavaGlobalRef java_callback, + const std::vector& responses) { + if (responses.empty() || responses.size() != 1u) { + PlainCallConnectAccountCallback(env, java_callback, false); + } else { + PlainCallConnectAccountCallback(env, java_callback, true); + } + }, + env, std::move(java_callback))); +} diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn index c6931f587366..9ef1144307ea 100644 --- a/build/android/BUILD.gn +++ b/build/android/BUILD.gn @@ -221,6 +221,7 @@ generate_jni("jni_headers") { "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/KeyringServiceFactory.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/SwapServiceFactory.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/TxServiceFactory.java", + "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/ConnectAccountFragment.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/permission/BraveDappPermissionPromptDialog.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstaller.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstallerUtil.java",