Connection.kt 3.91 KB
Newer Older
1
2
3
4
5
6
/* 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.browser.storage.sync

7
import androidx.annotation.GuardedBy
8
import mozilla.appservices.places.PlacesApi
9
10
import mozilla.appservices.places.PlacesReaderConnection
import mozilla.appservices.places.PlacesWriterConnection
11
import mozilla.components.concept.sync.SyncAuthInfo
12
import mozilla.components.support.sync.telemetry.SyncTelemetry
13
import java.io.Closeable
14
import java.io.File
15
16
17

const val DB_NAME = "places.sqlite"

18
19
20
21
22
23
24
25
26
/**
 * A slight abstraction over [PlacesApi].
 *
 * A single reader is assumed here, which isn't a limitation placed on use by [PlacesApi].
 * We can switch to pooling multiple readers as the need arises. Underneath, these are connections
 * to a SQLite database, and so opening and maintaining them comes with a memory/IO burden.
 *
 * Writer is always the same, as guaranteed by [PlacesApi].
 */
27
internal interface Connection : Closeable {
28
29
30
31
32
33
34
    /**
     * This should be removed. See: https://github.com/mozilla/application-services/issues/1877
     *
     * @return raw internal handle that could be used for referencing underlying [PlacesApi]. Use it with SyncManager.
     */
    fun getHandle(): Long

35
36
    fun reader(): PlacesReaderConnection
    fun writer(): PlacesWriterConnection
37
38
39
40
41

    // Until we get a real SyncManager in application-services libraries, we'll have to live with this
    // strange split that doesn't quite map all that well to our internal storage model.
    fun syncHistory(syncInfo: SyncAuthInfo)
    fun syncBookmarks(syncInfo: SyncAuthInfo)
42
43
44

    fun importVisitsFromFennec(dbPath: String)
    fun importBookmarksFromFennec(dbPath: String)
45
46
}

47
48
49
50
51
/**
 * A singleton implementation of the [Connection] interface backed by the Rust Places library.
 */
internal object RustPlacesConnection : Connection {
    @GuardedBy("this")
52
53
54
    private var api: PlacesApi? = null

    @GuardedBy("this")
55
    private var cachedReader: PlacesReaderConnection? = null
56
57

    /**
58
59
60
     * Creates a long-lived [PlacesApi] instance, and caches a reader connection.
     * Writer connection is maintained by [PlacesApi] itself, and is created upon its initialization.
     *
61
62
     * @param parentDir Location of the parent directory in which database is/will be stored.
     */
63
    fun init(parentDir: File) = synchronized(this) {
64
        if (api == null) {
65
            api = PlacesApi(File(parentDir, DB_NAME).canonicalPath)
66
        }
67
        cachedReader = api!!.openReader()
68
69
    }

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

75
    override fun reader(): PlacesReaderConnection = synchronized(this) {
76
77
        check(cachedReader != null) { "must call init first" }
        return cachedReader!!
78
79
    }

80
    override fun writer(): PlacesWriterConnection {
81
82
        check(api != null) { "must call init first" }
        return api!!.getWriter()
83
84
    }

85
86
    override fun syncHistory(syncInfo: SyncAuthInfo) {
        check(api != null) { "must call init first" }
87
        val ping = api!!.syncHistory(syncInfo.into())
88
        SyncTelemetry.processHistoryPing(ping)
89
90
91
    }

    override fun syncBookmarks(syncInfo: SyncAuthInfo) {
92
        check(api != null) { "must call init first" }
93
        val ping = api!!.syncBookmarks(syncInfo.into())
94
        SyncTelemetry.processBookmarksPing(ping)
95
96
    }

97
98
99
100
101
102
103
104
105
106
    override fun importVisitsFromFennec(dbPath: String) {
        check(api != null) { "must call init first" }
        api!!.importVisitsFromFennec(dbPath)
    }

    override fun importBookmarksFromFennec(dbPath: String) {
        check(api != null) { "must call init first" }
        api!!.importBookmarksFromFennec(dbPath)
    }

107
108
109
    override fun close() = synchronized(this) {
        check(api != null) { "must call init first" }
        api!!.close()
110
        api = null
111
    }
112
}