Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
Part 3: Rust SyncManager integration
Browse files Browse the repository at this point in the history
Co-authored-by: Arturo Mejia <arturomejiamarmol@gmail.com>
  • Loading branch information
Grisha Kruglov and Amejia481 committed Sep 26, 2019
1 parent f761880 commit fa0344c
Show file tree
Hide file tree
Showing 28 changed files with 747 additions and 175 deletions.
1 change: 1 addition & 0 deletions buildSrc/src/main/java/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ object Dependencies {

const val mozilla_sync_logins = "org.mozilla.appservices:logins:${Versions.mozilla_appservices}"
const val mozilla_places = "org.mozilla.appservices:places:${Versions.mozilla_appservices}"
const val mozilla_sync_manager = "org.mozilla.appservices:syncmanager:${Versions.mozilla_appservices}"

const val mozilla_push = "org.mozilla.appservices:push:${Versions.mozilla_appservices}"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ const val DB_NAME = "places.sqlite"
* Writer is always the same, as guaranteed by [PlacesApi].
*/
internal interface Connection : Closeable {
/**
* @return raw handle that could be used for referencing underlying [PlacesApi].
*/
fun getHandle(): Long

fun reader(): PlacesReaderConnection
fun writer(): PlacesWriterConnection

Expand Down Expand Up @@ -57,6 +62,11 @@ internal object RustPlacesConnection : Connection {
cachedReader = api!!.openReader()
}

override fun getHandle(): Long {
check(api != null) { "must call init first" }
return api!!.getHandle()
}

override fun reader(): PlacesReaderConnection = synchronized(this) {
check(cachedReader != null) { "must call init first" }
return cachedReader!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,11 @@ open class PlacesBookmarksStorage(context: Context) : PlacesStorage(context), Bo
}
}
}

/**
* TODO
*/
override fun getHandle(): Long {
return places.getHandle()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,11 @@ open class PlacesHistoryStorage(context: Context) : PlacesStorage(context), Hist
}
}
}

/**
* @return raw handle that could be used for referencing underlying [PlacesApi].
*/
override fun getHandle(): Long {
return places.getHandle()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,11 @@ class PlacesHistoryStorageTest {
fail()
}

override fun getHandle(): Long {
fail()
return 0L
}

override fun close() {
fail()
}
Expand Down Expand Up @@ -533,6 +538,11 @@ class PlacesHistoryStorageTest {

override fun syncBookmarks(syncInfo: SyncAuthInfo) {}

override fun getHandle(): Long {
fail()
return 0L
}

override fun close() {
fail()
}
Expand Down Expand Up @@ -565,6 +575,11 @@ class PlacesHistoryStorageTest {
fail()
}

override fun getHandle(): Long {
fail()
return 0L
}

override fun close() {
fail()
}
Expand Down Expand Up @@ -601,6 +616,11 @@ class PlacesHistoryStorageTest {
fail()
}

override fun getHandle(): Long {
fail()
return 0L
}

override fun close() {
fail()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@file:SuppressWarnings("TooManyFunctions")
package mozilla.components.concept.sync

import android.content.Context
import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.Deferred
import mozilla.components.support.base.observer.Observable
Expand Down Expand Up @@ -51,9 +52,10 @@ interface DeviceConstellation : Observable<DeviceEventsObserver> {
/**
* Set name of the current device.
* @param name New device name.
* @param context An application context, used for updating internal caches.
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun setDeviceNameAsync(name: String): Deferred<Boolean>
fun setDeviceNameAsync(name: String, context: Context): Deferred<Boolean>

/**
* Set a [DevicePushSubscription] for the current device.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ interface SyncableStore {
* @return [SyncStatus] A status object describing how sync went.
*/
suspend fun sync(authInfo: SyncAuthInfo): SyncStatus

/**
* TODO docs also, correct location?
*/
fun getHandle(): Long
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import mozilla.components.concept.sync.AuthType
import mozilla.components.service.fxa.FxaAuthData
import mozilla.components.service.fxa.SyncEngine
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.sync.toSyncEngines
import mozilla.components.service.fxa.toAuthType
import mozilla.components.support.base.feature.LifecycleAwareFeature
import mozilla.components.support.base.log.logger.Logger
Expand Down Expand Up @@ -248,19 +249,33 @@ class FxaWebChannelFeature(
return status
}

fun JSONArray.toStringList(): List<String> {
val result = mutableListOf<String>()
for (i in 0 until this.length()) {
result.add(this.getString(i))
}
return result
}

/**
* Handles the [COMMAND_OAUTH_LOGIN] event from the web-channel.
*/
private fun processOauthLoginCommand(accountManager: FxaAccountManager, payload: JSONObject): JSONObject? {
val authType: AuthType
val code: String
val state: String
val declinedEngines: List<String>?

try {
val data = payload.getJSONObject("data")
authType = data.getString("action").toAuthType()
code = data.getString("code")
state = data.getString("state")
declinedEngines = try {
data.getJSONArray("declinedSyncEngines").toStringList()
} catch (e: JSONException) {
null
}
} catch (e: JSONException) {
// TODO ideally, this should log to Sentry.
logger.error("Error while processing WebChannel oauth-login command", e)
Expand All @@ -270,7 +285,8 @@ class FxaWebChannelFeature(
accountManager.finishAuthenticationAsync(FxaAuthData(
authType = authType,
code = code,
state = state
state = state,
declinedEngines = declinedEngines?.toSyncEngines()
))

return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ class FxaWebChannelFeatureTest {
val messageHandler = argumentCaptor<MessageHandler>()
val responseToTheWebChannel = argumentCaptor<JSONObject>()
val port = mock<Port>()
val expectedEngines = setOf(SyncEngine.HISTORY)
val expectedEngines = setOf(SyncEngine.History)

WebExtensionController.installedExtensions[FxaWebChannelFeature.WEB_CHANNEL_EXTENSION_ID] = ext

Expand Down Expand Up @@ -250,7 +250,7 @@ class FxaWebChannelFeatureTest {
val messageHandler = argumentCaptor<MessageHandler>()
val responseToTheWebChannel = argumentCaptor<JSONObject>()
val port = mock<Port>()
val expectedEngines = setOf(SyncEngine.HISTORY, SyncEngine.BOOKMARKS, SyncEngine.PASSWORDS)
val expectedEngines = setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords)

WebExtensionController.installedExtensions[FxaWebChannelFeature.WEB_CHANNEL_EXTENSION_ID] = ext

Expand Down Expand Up @@ -301,7 +301,7 @@ class FxaWebChannelFeatureTest {
val messageHandler = argumentCaptor<MessageHandler>()
val responseToTheWebChannel = argumentCaptor<JSONObject>()
val port = mock<Port>()
val expectedEngines = setOf(SyncEngine.HISTORY, SyncEngine.BOOKMARKS, SyncEngine.PASSWORDS)
val expectedEngines = setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords)
val logoutDeferred = CompletableDeferred<Unit>()

WebExtensionController.installedExtensions[FxaWebChannelFeature.WEB_CHANNEL_EXTENSION_ID] = ext
Expand Down Expand Up @@ -425,7 +425,7 @@ class FxaWebChannelFeatureTest {
val messageHandler = argumentCaptor<MessageHandler>()
val responseToTheWebChannel = argumentCaptor<JSONObject>()
val port = mock<Port>()
val expectedEngines = setOf(SyncEngine.HISTORY, SyncEngine.BOOKMARKS, SyncEngine.PASSWORDS)
val expectedEngines = setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords)

WebExtensionController.installedExtensions[FxaWebChannelFeature.WEB_CHANNEL_EXTENSION_ID] = ext

Expand Down Expand Up @@ -477,7 +477,7 @@ class FxaWebChannelFeatureTest {
val messageHandler = argumentCaptor<MessageHandler>()
val responseToTheWebChannel = argumentCaptor<JSONObject>()
val port = mock<Port>()
val expectedEngines = setOf(SyncEngine.HISTORY, SyncEngine.BOOKMARKS, SyncEngine.PASSWORDS)
val expectedEngines = setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords)

WebExtensionController.installedExtensions[FxaWebChannelFeature.WEB_CHANNEL_EXTENSION_ID] = ext

Expand Down Expand Up @@ -648,7 +648,7 @@ class FxaWebChannelFeatureTest {
val messageHandler = argumentCaptor<MessageHandler>()
val jsonFromWebChannel = argumentCaptor<JSONObject>()
val port = mock<Port>()
val expectedEngines = setOf(SyncEngine.HISTORY, SyncEngine.BOOKMARKS)
val expectedEngines = setOf(SyncEngine.History, SyncEngine.Bookmarks)

WebExtensionController.installedExtensions[FxaWebChannelFeature.WEB_CHANNEL_EXTENSION_ID] = ext

Expand Down
5 changes: 5 additions & 0 deletions components/service/firefox-accounts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ Useful companion components:
* [feature-accounts](https://github.com/mozilla-mobile/android-components/tree/master/components/feature/accounts), provides a `tabs` integration on top of `FxaAccountManager`, to handle display of web sign-in UI.
* [browser-storage-sync](https://github.com/mozilla-mobile/android-components/tree/master/components/browser/storage-sync), provides data storage layers compatible with Firefox Sync.

## Before using this component
Products sending telemetry and using this component *must request* a data-review following [this process](https://wiki.mozilla.org/Firefox/Data_Collection).
This component provides data collection using the [Glean SDK](https://mozilla.github.io/glean/book/index.html).
The list of metrics being collected is available in the [metrics documentation](../../support/telemetry/docs/metrics.md).

## Usage
### Setting up the dependency

Expand Down
3 changes: 3 additions & 0 deletions components/service/firefox-accounts/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ dependencies {

// Parts of this dependency are typealiase'd or are otherwise part of this module's public API.
api Dependencies.mozilla_fxa
implementation Dependencies.mozilla_sync_manager

// Observable is part of public API of the FxaAccountManager.
api project(':support-base')
implementation project(':support-sync-telemetry')
implementation project(':service-glean')
implementation project(':support-ktx')

implementation Dependencies.kotlin_stdlib
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ data class SyncConfig(
* robust integrations. We do not expose "unknown" engines via our public API, but do handle them
* internally (by persisting their enabled/disabled status).
*/
enum class SyncEngine(val nativeName: String) {
HISTORY("history"),
BOOKMARKS("bookmarks"),
PASSWORDS("passwords"),
sealed class SyncEngine(val nativeName: String) {
object History : SyncEngine("history")
object Bookmarks : SyncEngine("bookmarks")
object Passwords : SyncEngine("passwords")
data class Other(val name: String) : SyncEngine(name)

internal object Forms : SyncEngine("forms")
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package mozilla.components.service.fxa

import android.content.Context
import androidx.annotation.GuardedBy
import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -81,11 +82,12 @@ class FxaDeviceConstellation(
deviceObserverRegistry.register(observer, owner, autoPause)
}

override fun setDeviceNameAsync(name: String): Deferred<Boolean> {
override fun setDeviceNameAsync(name: String, context: Context): Deferred<Boolean> {
return scope.async {
val rename = handleFxaExceptions(logger, "changing device name") {
account.setDeviceDisplayName(name)
}
FxaDeviceSettingsCache(context).updateCachedName(name)
// See the latest device (name) changes after changing it.
val refreshDevices = refreshDevicesAsync().await()

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

import android.content.Context
import android.content.SharedPreferences
import mozilla.appservices.syncmanager.DeviceSettings
import mozilla.appservices.syncmanager.DeviceType
import mozilla.components.support.base.log.logger.Logger
import org.json.JSONObject
import java.lang.IllegalArgumentException
import java.lang.IllegalStateException

private const val CACHE_NAME = "FxaDeviceSettingsCache"
private const val CACHE_KEY = CACHE_NAME
private const val KEY_FXA_DEVICE_ID = "kid"
private const val KEY_DEVICE_NAME = "syncKey"
private const val KEY_DEVICE_TYPE = "tokenServerUrl"

/**
* A thin wrapper around [SharedPreferences] which knows how to serialize/deserialize [DeviceSettings].
*
* This class exists to provide background sync workers with access to [DeviceSettings].
*/
class FxaDeviceSettingsCache(context: Context) : SharedPreferencesCache<DeviceSettings>(context) {
override val logger = Logger("SyncAuthInfoCache")
override val cacheKey = CACHE_KEY
override val cacheName = CACHE_NAME

override fun DeviceSettings.toJSON(): JSONObject {
return JSONObject().also {
it.put(KEY_FXA_DEVICE_ID, this.fxaDeviceId)
it.put(KEY_DEVICE_NAME, this.name)
it.put(KEY_DEVICE_TYPE, this.type.name)
}
}

override fun fromJSON(obj: JSONObject): DeviceSettings {
return DeviceSettings(
fxaDeviceId = obj.getString(KEY_FXA_DEVICE_ID),
name = obj.getString(KEY_DEVICE_NAME),
type = obj.getString(KEY_DEVICE_TYPE).toDeviceType()
)
}

fun updateCachedName(name: String) {
val cached = getCached() ?: throw IllegalStateException("Trying to update cached value in an empty cache")
setToCache(cached.copy(name = name))
}

private fun String.toDeviceType(): DeviceType {
return when (this) {
"DESKTOP" -> DeviceType.DESKTOP
"MOBILE" -> DeviceType.MOBILE
"TABLET" -> DeviceType.TABLET
"VR" -> DeviceType.VR
"TV" -> DeviceType.TV
else -> throw IllegalArgumentException("Unknown device type in cached string: $this")
}
}
}
Loading

0 comments on commit fa0344c

Please sign in to comment.