diff --git a/mobile/android/android-components/.buildconfig.yml b/mobile/android/android-components/.buildconfig.yml
index 3d95247a97d86..474e227b9e912 100644
--- a/mobile/android/android-components/.buildconfig.yml
+++ b/mobile/android/android-components/.buildconfig.yml
@@ -533,6 +533,13 @@ projects:
- components:support-test
- components:support-utils
- components:tooling-lint
+ components:concept-passwords-file:
+ description: An abstract definition of a component for importing passwords from
+ a file.
+ path: components/concept/passwords-file
+ publish: true
+ upstream_dependencies:
+ - components:tooling-lint
components:concept-push:
description: An abstract definition of a push service component.
path: components/concept/push
@@ -2033,6 +2040,14 @@ projects:
- components:support-utils
- components:tooling-lint
- components:ui-icons
+ components:lib-passwords-file:
+ description: A concrete implementation of concept-passwords-file that imports
+ passwords from CSV files.
+ path: components/lib/passwords-file
+ publish: true
+ upstream_dependencies:
+ - components:concept-passwords-file
+ - components:tooling-lint
components:lib-publicsuffixlist:
description: A library for reading and using the public suffix list.
path: components/lib/publicsuffixlist
diff --git a/mobile/android/android-components/components/concept/passwords-file/build.gradle b/mobile/android/android-components/components/concept/passwords-file/build.gradle
new file mode 100644
index 0000000000000..f908ada70391e
--- /dev/null
+++ b/mobile/android/android-components/components/concept/passwords-file/build.gradle
@@ -0,0 +1,24 @@
+/* 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/. */
+
+plugins {
+ alias(libs.plugins.dependency.analysis)
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ namespace = 'mozilla.components.concept.passwords.file'
+}
+
+dependencies {
+ testImplementation libs.androidx.test.junit
+ testImplementation libs.kotlinx.coroutines.test
+ testImplementation libs.robolectric
+}
+
+apply from: '../../../common-config.gradle'
+apply from: '../../../publish.gradle'
+ext.configurePublish(config.componentsGroupId, project.name, project.ext.description)
diff --git a/mobile/android/android-components/components/concept/passwords-file/lint-baseline.xml b/mobile/android/android-components/components/concept/passwords-file/lint-baseline.xml
new file mode 100644
index 0000000000000..dc0eec78f4b10
--- /dev/null
+++ b/mobile/android/android-components/components/concept/passwords-file/lint-baseline.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/mobile/android/android-components/components/concept/passwords-file/src/main/AndroidManifest.xml b/mobile/android/android-components/components/concept/passwords-file/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000..e16cda1d34462
--- /dev/null
+++ b/mobile/android/android-components/components/concept/passwords-file/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/mobile/android/android-components/components/concept/passwords-file/src/main/java/mozilla/components/concept/passwords/file/PasswordsFileImporter.kt b/mobile/android/android-components/components/concept/passwords-file/src/main/java/mozilla/components/concept/passwords/file/PasswordsFileImporter.kt
new file mode 100644
index 0000000000000..54951f3997914
--- /dev/null
+++ b/mobile/android/android-components/components/concept/passwords-file/src/main/java/mozilla/components/concept/passwords/file/PasswordsFileImporter.kt
@@ -0,0 +1,47 @@
+/* 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/. */
+
+package mozilla.components.concept.passwords.file
+
+import android.net.Uri
+
+/**
+ * An interface for importing passwords from a file.
+ */
+fun interface PasswordsFileImporter {
+ /**
+ * The result of a passwords file import.
+ *
+ * @property count The number of passwords imported.
+ */
+ data class ImportResult(val count: Int)
+
+ /**
+ * Imports passwords from the file at the given [uri].
+ *
+ * @param uri The URI of the file to import passwords from.
+ * @return A [Result] containing [ImportResult] on success or an exception on failure.
+ */
+ suspend fun importPasswordsFromUri(uri: Uri): Result
+
+ companion object {
+ /**
+ * Creates a [PasswordsFileImporter] that always returns a successful [Result].
+ *
+ * @param result The [ImportResult] to return on every import.
+ */
+ fun alwaysSuccess(
+ result: ImportResult = ImportResult(count = 0),
+ ): PasswordsFileImporter = PasswordsFileImporter { Result.success(result) }
+
+ /**
+ * Creates a [PasswordsFileImporter] that always returns a failed [Result].
+ *
+ * @param exception The [Exception] to return on every import.
+ */
+ fun alwaysFailure(
+ exception: Exception = IllegalStateException("Import failed"),
+ ): PasswordsFileImporter = PasswordsFileImporter { Result.failure(exception) }
+ }
+}
diff --git a/mobile/android/android-components/components/lib/passwords-file/build.gradle b/mobile/android/android-components/components/lib/passwords-file/build.gradle
new file mode 100644
index 0000000000000..a182c18241b8b
--- /dev/null
+++ b/mobile/android/android-components/components/lib/passwords-file/build.gradle
@@ -0,0 +1,24 @@
+/* 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/. */
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ namespace = 'mozilla.components.lib.passwords.file'
+}
+
+dependencies {
+ api project(':components:concept-passwords-file')
+ implementation libs.kotlinx.coroutines
+
+ testImplementation platform(libs.junit.bom)
+ testImplementation libs.junit4
+ testImplementation libs.kotlinx.coroutines.test
+ testImplementation libs.robolectric
+}
+
+apply from: '../../../common-config.gradle'
+apply from: '../../../publish.gradle'
+ext.configurePublish(config.componentsGroupId, project.name, project.ext.description)
diff --git a/mobile/android/android-components/components/lib/passwords-file/lint-baseline.xml b/mobile/android/android-components/components/lib/passwords-file/lint-baseline.xml
new file mode 100644
index 0000000000000..dc0eec78f4b10
--- /dev/null
+++ b/mobile/android/android-components/components/lib/passwords-file/lint-baseline.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/mobile/android/android-components/components/lib/passwords-file/src/main/AndroidManifest.xml b/mobile/android/android-components/components/lib/passwords-file/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000..e16cda1d34462
--- /dev/null
+++ b/mobile/android/android-components/components/lib/passwords-file/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/mobile/android/android-components/components/lib/passwords-file/src/main/java/mozilla/components/lib/passwords/file/CsvPasswordsFileImporter.kt b/mobile/android/android-components/components/lib/passwords-file/src/main/java/mozilla/components/lib/passwords/file/CsvPasswordsFileImporter.kt
new file mode 100644
index 0000000000000..9e9f62de2d03b
--- /dev/null
+++ b/mobile/android/android-components/components/lib/passwords-file/src/main/java/mozilla/components/lib/passwords/file/CsvPasswordsFileImporter.kt
@@ -0,0 +1,19 @@
+/* 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/. */
+
+package mozilla.components.lib.passwords.file
+
+import android.net.Uri
+import mozilla.components.concept.passwords.file.PasswordsFileImporter
+import mozilla.components.concept.passwords.file.PasswordsFileImporter.ImportResult
+
+/**
+ * A [PasswordsFileImporter] for CSV password files.
+ *
+ * This is a stub implementation. CSV parsing and storage insertion will be added in a follow-up.
+ */
+class CsvPasswordsFileImporter : PasswordsFileImporter {
+ override suspend fun importPasswordsFromUri(uri: Uri): Result =
+ Result.success(ImportResult(count = 0))
+}
diff --git a/mobile/android/android-components/components/lib/passwords-file/src/test/java/mozilla/components/lib/passwords/file/CsvPasswordsFileImporterTest.kt b/mobile/android/android-components/components/lib/passwords-file/src/test/java/mozilla/components/lib/passwords/file/CsvPasswordsFileImporterTest.kt
new file mode 100644
index 0000000000000..0dc383d018bb5
--- /dev/null
+++ b/mobile/android/android-components/components/lib/passwords-file/src/test/java/mozilla/components/lib/passwords/file/CsvPasswordsFileImporterTest.kt
@@ -0,0 +1,27 @@
+/* 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/. */
+
+package mozilla.components.lib.passwords.file
+
+import android.net.Uri
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class CsvPasswordsFileImporterTest {
+
+ @Test
+ fun `importPasswordsFromUri returns success with zero count`() = runTest {
+ val importer = CsvPasswordsFileImporter()
+
+ val result = importer.importPasswordsFromUri(Uri.EMPTY)
+
+ assertTrue(result.isSuccess)
+ assertEquals(0, result.getOrThrow().count)
+ }
+}
diff --git a/mobile/android/android-components/components/lib/passwords-file/src/test/resources/robolectric.properties b/mobile/android/android-components/components/lib/passwords-file/src/test/resources/robolectric.properties
new file mode 100644
index 0000000000000..3f67ea5ac1bf1
--- /dev/null
+++ b/mobile/android/android-components/components/lib/passwords-file/src/test/resources/robolectric.properties
@@ -0,0 +1 @@
+sdk=35
diff --git a/taskcluster/config.yml b/taskcluster/config.yml
index df334cdfa2889..0d41192924370 100644
--- a/taskcluster/config.yml
+++ b/taskcluster/config.yml
@@ -251,6 +251,7 @@ treeherder:
'concept-fetch': 'concept-fetch'
'concept-llm': 'concept-llm'
'concept-menu': 'concept-menu'
+ 'concept-passwords-file': 'concept-passwords-file'
'concept-push': 'concept-push'
'concept-storage': 'concept-storage'
'concept-storage-bookmarks': 'concept-storage-bookmarks'
@@ -310,6 +311,7 @@ treeherder:
'lib-llm-gemininano': 'lib-llm-gemininano'
'lib-llm-mlpa': 'lib-llm-mlpa'
'lib-integrity-googleplay': 'lib-integrity-googleplay'
+ 'lib-passwords-file': 'lib-passwords-file'
'lib-shake': 'lib-shake'
'lib-publicsuffixlist': 'lib-publicsuffixlist'
'lib-push-firebase': 'lib-push-firebase'