From 1abdac0aff0916b1e4a4bd52f1896eec00834262 Mon Sep 17 00:00:00 2001 From: Carmelo Messina Date: Mon, 26 May 2025 17:05:50 +0200 Subject: [PATCH] Android Import Passwords: Adds the possibility of importing a csv file containing the list of passwords, with the same functionality available on desktops (#2054) --- build/cromite_patches_list.txt | 1 + build/patches/Android-Import-Passwords.patch | 353 +++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 build/patches/Android-Import-Passwords.patch diff --git a/build/cromite_patches_list.txt b/build/cromite_patches_list.txt index cabdd877..c225e21d 100644 --- a/build/cromite_patches_list.txt +++ b/build/cromite_patches_list.txt @@ -298,6 +298,7 @@ Enable-component-updater.patch Android-fonts-fingerprinting-mitigation.patch Disable-Android-Tab-Declutter.patch Disable-CSSDynamicRangeLimit.patch +Android-Import-Passwords.patch # temporary or wip patches Temp-PerformanceNavigationTiming-privacy-fix.patch diff --git a/build/patches/Android-Import-Passwords.patch b/build/patches/Android-Import-Passwords.patch new file mode 100644 index 00000000..d5ea213f --- /dev/null +++ b/build/patches/Android-Import-Passwords.patch @@ -0,0 +1,353 @@ +From: uazo +Date: Mon, 26 May 2025 12:17:48 +0000 +Subject: Android Import Passwords + +Adds the possibility of importing a csv file containing the list of passwords, +with the same functionality available on desktops. + +License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html +--- + ...e_password_preferences_action_bar_menu.xml | 6 ++ + .../settings/PasswordSettings.java | 34 +++++++++ + .../settings/PasswordManagerHandler.java | 3 + + .../settings/PasswordUiView.java | 8 +++ + .../android/password_ui_view_android.cc | 71 +++++++++++++++++++ + .../android/password_ui_view_android.h | 20 +++++- + .../Import-Password-Android.grdp | 9 +++ + .../core/browser/import/password_importer.cc | 11 +++ + 8 files changed, 161 insertions(+), 1 deletion(-) + create mode 100644 chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Import-Password-Android.grdp + +diff --git a/chrome/android/java/res/menu/save_password_preferences_action_bar_menu.xml b/chrome/android/java/res/menu/save_password_preferences_action_bar_menu.xml +--- a/chrome/android/java/res/menu/save_password_preferences_action_bar_menu.xml ++++ b/chrome/android/java/res/menu/save_password_preferences_action_bar_menu.xml +@@ -22,6 +22,12 @@ found in the LICENSE file. + android:visibility="gone" + app:showAsAction="ifRoom"/> + ++ ++ + ListenerDestroyed(); + } + + void PasswordUiViewAndroid::Destroy(JNIEnv*, const JavaRef&) { +@@ -314,6 +321,70 @@ jboolean PasswordUiViewAndroid::IsWaitingForPasswordStore( + return saved_passwords_presenter_.IsWaitingForPasswordStore(); + } + ++void PasswordUiViewAndroid::ImportFinished( ++ const password_manager::ImportResults& results) { ++ std::string error; ++ if (results.status == password_manager::ImportResults::Status::SUCCESS) ++ error = "Success."; ++ else if (results.status == password_manager::ImportResults::Status::IO_ERROR) ++ error = "Failed to read provided file."; ++ else if (results.status == password_manager::ImportResults::Status::BAD_FORMAT) ++ error = "Header is missing, invalid or could not be read."; ++ else if (results.status == password_manager::ImportResults::Status::NUM_PASSWORDS_EXCEEDED) ++ error = "Too many passwords."; ++ else ++ error = "UNKNOWN ERROR."; ++ ++ std::stringstream message; ++ message << error << " Imported " << results.number_imported << " passwords."; ++ auto result = message.str(); ++ ++ select_file_dialog_->ShowToast(result); ++ ++ importer_.reset(); ++} ++ ++jboolean PasswordUiViewAndroid::StartImporting( ++ JNIEnv* env, ++ const base::android::JavaRef& obj, ++ const JavaParamRef& java_window) { ++ ui::WindowAndroid* window = ++ ui::WindowAndroid::FromJavaWindowAndroid(java_window); ++ CHECK(window); ++ ++ select_file_dialog_ = ui::SelectFileDialog::Create( ++ this, std::make_unique(nullptr)); ++ ++ ui::SelectFileDialog::FileTypeInfo file_type_info; ++ ++ const std::vector v_accept_types = { u"application/octet-stream" }; ++ select_file_dialog_->SetAcceptTypes(v_accept_types); ++ ++ select_file_dialog_->SelectFile( ++ ui::SelectFileDialog::SELECT_OPEN_FILE, ++ std::u16string(), ++ base::FilePath(), ++ &file_type_info, ++ 0, ++ base::FilePath::StringType(), ++ window); ++ ++ return true; ++} ++ ++void PasswordUiViewAndroid::FileSelectionCanceled() {} ++ ++void PasswordUiViewAndroid::FileSelected(const ui::SelectedFileInfo& file, int index) { ++ base::FilePath path = file.path(); ++ ++ importer_ = ++ std::make_unique(&saved_passwords_presenter_); ++ importer_->Import(path, ++ password_manager::PasswordForm::Store::kProfileStore, ++ base::BindOnce(&PasswordUiViewAndroid::ImportFinished, ++ base::Unretained(this))); ++} ++ + // static + static jlong JNI_PasswordUiView_Init(JNIEnv* env, + const JavaParamRef& obj, +diff --git a/chrome/browser/password_manager/android/password_ui_view_android.h b/chrome/browser/password_manager/android/password_ui_view_android.h +--- a/chrome/browser/password_manager/android/password_ui_view_android.h ++++ b/chrome/browser/password_manager/android/password_ui_view_android.h +@@ -21,6 +21,8 @@ + #include "chrome/browser/signin/identity_manager_factory.h" + #include "components/password_manager/core/browser/password_store/password_store_consumer.h" + #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h" ++#include "components/password_manager/core/browser/import/password_importer.h" ++#include "ui/shell_dialogs/select_file_dialog.h" + + class Profile; + +@@ -31,7 +33,8 @@ class CredentialProviderInterface; + // PasswordUIView for Android, contains jni hooks that allows Android UI to + // display passwords and route UI commands back to SavedPasswordsPresenter. + class PasswordUiViewAndroid +- : public password_manager::SavedPasswordsPresenter::Observer { ++ : public password_manager::SavedPasswordsPresenter::Observer, ++ public ui::SelectFileDialog::Listener { + public: + // Result of transforming a vector of PasswordForms into their CSV + // description and writing that to disk. +@@ -95,6 +98,18 @@ class PasswordUiViewAndroid + const base::android::JavaParamRef& obj); + jboolean IsWaitingForPasswordStore(JNIEnv* env, + const base::android::JavaRef&); ++ ++ void FileSelected(const ui::SelectedFileInfo& file, ++ int index) override; ++ void FileSelectionCanceled() override; ++ ++ void ImportFinished( ++ const password_manager::ImportResults& results); ++ ++ jboolean StartImporting(JNIEnv* env, ++ const base::android::JavaRef& obj, ++ const base::android::JavaParamRef& java_window); ++ + // Destroy the native implementation. + void Destroy(JNIEnv*, const base::android::JavaRef&); + +@@ -162,6 +177,9 @@ class PasswordUiViewAndroid + std::vector passwords_; + std::vector blocked_sites_; + ++ scoped_refptr select_file_dialog_; ++ std::unique_ptr importer_; ++ + // If not null, passwords for exporting will be obtained from + // |*credential_provider_for_testing_|, otherwise from + // |saved_passwords_presenter_|. This must remain null in production code. +diff --git a/chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Import-Password-Android.grdp b/chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Import-Password-Android.grdp +new file mode 100644 +--- /dev/null ++++ b/chrome/browser/ui/android/strings/cromite_android_chrome_strings_grd/Import-Password-Android.grdp +@@ -0,0 +1,9 @@ ++ ++ ++ ++ Import passwords… ++ ++ ++ Import passwords stored with Chrome ++ ++ +diff --git a/components/password_manager/core/browser/import/password_importer.cc b/components/password_manager/core/browser/import/password_importer.cc +--- a/components/password_manager/core/browser/import/password_importer.cc ++++ b/components/password_manager/core/browser/import/password_importer.cc +@@ -75,6 +75,17 @@ const int32_t kMaxFileSizeBytes = 150 * 1024; + // optional string. The string will be present if the status is SUCCESS. + base::expected ReadFileToString( + const base::FilePath& path) { ++#if BUILDFLAG(IS_ANDROID) ++ if (path.IsContentUri()) { ++ base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); ++ auto fileLength = file.GetLength(); ++ std::vector buffer(fileLength); ++ file.ReadAtCurrentPos(base::as_writable_byte_span(buffer)); ++ ++ std::string file_contents(buffer.begin(), buffer.end()); ++ return std::move(file_contents); ++ } ++#endif + std::optional file_size = base::GetFileSize(path); + + if (file_size.has_value()) { +--