Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mobile/android/android-components/.buildconfig.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2284,6 +2284,7 @@ projects:
- components:lib-publicsuffixlist
- components:support-base
- components:support-ktx
- components:support-test
- components:support-utils
- components:tooling-lint
components:support-android-test:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import mozilla.components.concept.storage.SearchResult
import mozilla.components.concept.storage.TopFrecentSiteInfo
import mozilla.components.concept.storage.VisitInfo
import mozilla.components.concept.storage.VisitType
import mozilla.components.concept.storage.constraints
import mozilla.components.concept.storage.periodicStorageWorkRequest
import mozilla.components.concept.sync.SyncAuthInfo
import mozilla.components.concept.sync.SyncStatus
import mozilla.components.concept.sync.SyncableStore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package mozilla.components.browser.storage.sync

import android.content.Context
import androidx.work.WorkerParameters
import mozilla.components.concept.storage.StorageMaintenanceWorker
import mozilla.components.support.base.log.logger.Logger

/**
Expand All @@ -14,7 +15,7 @@ import mozilla.components.support.base.log.logger.Logger
* If there is a failure or the worker constraints are no longer met during execution,
* active write operations on [PlacesStorage] are cancelled.
*
* See also [StorageMaintenanceWorker].
* See also [mozilla.components.concept.storage.StorageMaintenanceWorker].
*/
internal class PlacesHistoryStorageWorker(context: Context, params: WorkerParameters) :
StorageMaintenanceWorker(context, params) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import mozilla.components.concept.storage.HistoryMetadataKey
import mozilla.components.concept.storage.HistoryMetadataObservation
import mozilla.components.concept.storage.PageObservation
import mozilla.components.concept.storage.PageVisit
import mozilla.components.concept.storage.StorageMaintenanceWorker
import mozilla.components.concept.storage.VisitType
import mozilla.components.concept.storage.constraints
import mozilla.components.concept.storage.periodicStorageWorkRequest
import mozilla.components.concept.sync.SyncAuthInfo
import mozilla.components.concept.sync.SyncStatus
import mozilla.components.support.test.any
Expand Down Expand Up @@ -619,7 +622,10 @@ class PlacesHistoryStorageTest {
}

assertEquals(request.workSpec.isPeriodic, true)
assertEquals(request.workSpec.intervalDuration, TimeUnit.HOURS.toMillis(StorageMaintenanceWorker.WORKER_PERIOD_IN_HOURS))
assertEquals(
request.workSpec.intervalDuration,
TimeUnit.HOURS.toMillis(StorageMaintenanceWorker.WORKER_PERIOD_IN_HOURS),
)
assertEquals(request.workSpec.constraints.requiresBatteryNotLow(), true)
assertEquals(request.workSpec.constraints.requiresDeviceIdle(), true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ dependencies {
// Included via 'api' because this module is unusable without coroutines.
api libs.kotlinx.coroutines

implementation project(':components:support-base')
implementation project(':components:support-ktx')
implementation project(':components:support-utils')

implementation libs.androidx.annotation
implementation libs.androidx.work.runtime

testImplementation project(':components:support-test')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import java.util.Locale
/**
* An interface which defines read/write methods for credit card and address data.
*/
interface CreditCardsAddressesStorage {
interface CreditCardsAddressesStorage : Storage, StorageMaintenanceRegistry {

/**
* Inserts the provided credit card into the database, and returns
Expand Down Expand Up @@ -126,6 +126,22 @@ interface CreditCardsAddressesStorage {
* Removes any encrypted data from this storage. Useful after encountering key loss.
*/
suspend fun scrubEncryptedData()

override suspend fun runMaintenance(dbSizeLimit: UInt) {
// Implemented by concrete implementation of `CreditCardsAddressesStorage`
}

override suspend fun warmUp() {
// Implemented by concrete implementation of `CreditCardsAddressesStorage`
}

override fun registerStorageMaintenanceWorker() {
// Implemented by concrete implementation of `CreditCardsAddressesStorage`
}

override fun unregisterStorageMaintenanceWorker(uniqueWorkName: String) {
// Implemented by concrete implementation of `CreditCardsAddressesStorage`
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ data class EncryptedLogin(
/**
* An interface describing a storage layer for logins/passwords.
*/
interface LoginsStorage : AutoCloseable {
interface LoginsStorage : Storage, StorageMaintenanceRegistry, AutoCloseable {
/**
* Clears out all local state, bringing us back to the state before the first write (or sync).
*/
Expand Down Expand Up @@ -251,6 +251,22 @@ interface LoginsStorage : AutoCloseable {
* @return A list of [Login] objects, representing matching logins.
*/
suspend fun getByBaseDomain(origin: String): List<Login>

override fun registerStorageMaintenanceWorker() {
// Implemented by concrete implementation of `LoginsStorage`
}

override fun unregisterStorageMaintenanceWorker(uniqueWorkName: String) {
// Implemented by concrete implementation of `LoginsStorage`
}

override suspend fun runMaintenance(dbSizeLimit: UInt) {
// Implemented by concrete implementation of `LoginsStorage`
}

override suspend fun warmUp() {
// Implemented by concrete implementation of `LoginsStorage`
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
* 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.browser.storage.sync
package mozilla.components.concept.storage

import androidx.work.Constraints
import androidx.work.PeriodicWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import mozilla.components.concept.storage.Storage
import java.util.concurrent.TimeUnit

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* 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.browser.storage.sync
package mozilla.components.concept.storage

import android.content.Context
import androidx.work.CoroutineWorker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ android {

dependencies {
api ComponentsDependencies.mozilla_appservices_autofill
implementation libs.androidx.work.runtime

api project(':components:concept-base')
api project(':components:concept-storage')
Expand All @@ -30,6 +31,7 @@ dependencies {
testImplementation ComponentsDependencies.mozilla_appservices_init_rust_components
testImplementation libs.androidx.test.core
testImplementation libs.androidx.test.junit
testImplementation libs.androidx.work.testing
testImplementation libs.kotlinx.coroutines.test
testImplementation libs.robolectric
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package mozilla.components.service.sync.autofill
import android.content.Context
import androidx.annotation.GuardedBy
import androidx.annotation.VisibleForTesting
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.WorkManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.withContext
Expand All @@ -18,6 +20,8 @@ import mozilla.components.concept.storage.CreditCardsAddressesStorage
import mozilla.components.concept.storage.NewCreditCardFields
import mozilla.components.concept.storage.UpdatableAddressFields
import mozilla.components.concept.storage.UpdatableCreditCardFields
import mozilla.components.concept.storage.constraints
import mozilla.components.concept.storage.periodicStorageWorkRequest
import mozilla.components.concept.sync.SyncableStore
import mozilla.components.lib.dataprotect.SecureAbove22Preferences
import mozilla.components.support.base.log.logger.Logger
Expand All @@ -36,7 +40,7 @@ const val AUTOFILL_DB_NAME = "autofill.sqlite"
* Used for storing encryption key material.
*/
class AutofillCreditCardsAddressesStorage(
context: Context,
private val context: Context,
securePrefs: Lazy<SecureAbove22Preferences>,
) : CreditCardsAddressesStorage, SyncableStore, AutoCloseable {
private val logger = Logger("AutofillCCAddressesStorage")
Expand All @@ -54,11 +58,15 @@ class AutofillCreditCardsAddressesStorage(
/**
* "Warms up" this storage layer by establishing the database connection.
*/
suspend fun warmUp() = withContext(coroutineContext) {
override suspend fun warmUp() = withContext(coroutineContext) {
logElapsedTime(logger, "Warming up storage") { conn }
Unit
}

override suspend fun runMaintenance(dbSizeLimit: UInt) {
conn.getStorage().runMaintenance()
}

override suspend fun addCreditCard(
creditCardFields: NewCreditCardFields,
): CreditCard = withContext(coroutineContext) {
Expand Down Expand Up @@ -181,6 +189,31 @@ class AutofillCreditCardsAddressesStorage(
coroutineContext.cancel()
conn.close()
}

/**
* Enqueues a periodic storage maintenance worker to WorkManager.
*/
override fun registerStorageMaintenanceWorker() {
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
AutofillStorageWorker.UNIQUE_NAME,
ExistingPeriodicWorkPolicy.KEEP,
periodicStorageWorkRequest<AutofillStorageWorker>(
tag = AutofillStorageWorker.UNIQUE_NAME,
) {
constraints {
setRequiresBatteryNotLow(true)
setRequiresDeviceIdle(true)
}
},
)
}

override fun unregisterStorageMaintenanceWorker(uniqueWorkName: String) {
WorkManager.getInstance(context).also {
it.cancelUniqueWork(AutofillStorageWorker.UNIQUE_NAME)
it.cancelAllWorkByTag(AutofillStorageWorker.UNIQUE_NAME)
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* 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.service.sync.autofill

import android.content.Context
import androidx.work.WorkerParameters
import mozilla.components.concept.storage.StorageMaintenanceWorker
import mozilla.components.support.base.log.logger.Logger

/**
* A WorkManager Worker that executes [AutofillCreditCardsAddressesStorage.runMaintenance].
*
* If there is a failure or the worker constraints are no longer met during execution,
* active write operations on [AutofillCreditCardsAddressesStorage] are cancelled.
*
* See also [mozilla.components.concept.storage.StorageMaintenanceWorker].
*/
internal class AutofillStorageWorker(context: Context, params: WorkerParameters) :
StorageMaintenanceWorker(context, params) {

val logger = Logger(AUTOFILL_STORAGE_WORKER_TAG)

override suspend fun operate() {
GlobalAutofillDependencyProvider.requireAutofillStorage()
.runMaintenance(DB_SIZE_LIMIT_IN_BYTES.toUInt())
}

override fun onError(exception: Exception) {
GlobalAutofillDependencyProvider.requireAutofillStorage().cancelWrites()
logger.error("An exception occurred while running the maintenance task: ${exception.message}")
}

companion object {
private const val IDENTIFIER_PREFIX = "mozilla.components.service.sync.autofill"
private const val AUTOFILL_STORAGE_WORKER_TAG = "$IDENTIFIER_PREFIX.AutofillStorageWorker"

internal const val UNIQUE_NAME = "$IDENTIFIER_PREFIX.AutofillStorageWorker"

// The implementation of `runMaintenance` on `DatabaseLoginsStorage` doesn't take any input
// but `Storage` requires that we pass a value.
internal const val DB_SIZE_LIMIT_IN_BYTES = Int.MAX_VALUE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* 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.service.sync.autofill

import androidx.annotation.VisibleForTesting
import mozilla.components.concept.storage.CreditCardsAddressesStorage
import mozilla.components.concept.storage.LoginsStorage

/**
* Provides global access to the dependencies needed for autofill storage operations.
* */
object GlobalAutofillDependencyProvider {

@VisibleForTesting
internal var autofillStorage: Lazy<CreditCardsAddressesStorage>? = null

/**
* Initializes logins storage for running the maintenance task via [AutofillStorageWorker].
* This method should be called in client application's onCreate method and before
* [AutofillCreditCardsAddressesStorage.registerStorageMaintenanceWorker] in order to run the worker while
* the app is not running.
* */
fun initialize(autofillStorage: Lazy<CreditCardsAddressesStorage>) {
this.autofillStorage = autofillStorage
}

/**
* Provides [LoginsStorage] globally when needed for [AutofillStorageWorker]
* to run maintenance on the storage.
* */
internal fun requireAutofillStorage(): CreditCardsAddressesStorage {
return requireNotNull(autofillStorage?.value) {
"GlobalAutofillDependencyProvider.initialize must be called before accessing the Autofill storage"
}
}
}
Loading