feat: add in config storage and injection to common places, managing lifecycle of native instances

This commit is contained in:
0x330a 2023-01-17 17:27:54 +11:00
parent 0729bdab20
commit 00fe77af8a
No known key found for this signature in database
GPG Key ID: 267811D6E6A2698C
12 changed files with 161 additions and 20 deletions

View File

@ -58,6 +58,7 @@ import org.thoughtcrime.securesms.database.JobDatabase;
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
import org.thoughtcrime.securesms.database.Storage;
import org.thoughtcrime.securesms.database.model.EmojiSearchData;
import org.thoughtcrime.securesms.dependencies.ConfigFactory;
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
import org.thoughtcrime.securesms.dependencies.DatabaseModule;
import org.thoughtcrime.securesms.emoji.EmojiSource;
@ -147,6 +148,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
@Inject MessageDataProvider messageDataProvider;
@Inject JobDatabase jobDatabase;
@Inject TextSecurePreferences textSecurePreferences;
@Inject ConfigFactory configFactory;
CallMessageProcessor callMessageProcessor;
MessagingModuleConfiguration messagingModuleConfiguration;
@ -460,7 +462,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
poller.setUserPublicKey(userPublicKey);
return;
}
poller = new Poller();
poller = new Poller(configFactory);
}
public void startPollingIfNeeded() {
@ -538,6 +540,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
if (!deleteDatabase("signal.db")) {
Log.d("Loki", "Failed to delete database.");
}
configFactory.keyPairChanged();
Util.runOnMain(() -> new Handler().postDelayed(ApplicationContext.this::restartApplication, 200));
}

View File

@ -1,13 +1,43 @@
package org.thoughtcrime.securesms.database
import android.content.Context
import androidx.core.content.contentValuesOf
import androidx.core.database.getBlobOrNull
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(context, helper) {
companion object {
private const val KEY = "key"
private const val VALUE = "value"
private const val TABLE_NAME = "configs_table"
const val CREATE_CONFIG_TABLE_COMMAND = "CREATE TABLE $TABLE_NAME ($KEY TEXT NOT NULL, $VALUE BLOB NOT NULL, PRIMARY KEY($KEY));"
private const val KEY_WHERE = "$KEY = ?"
const val USER_KEY = "user"
const val CONTACTS_KEY = "contacts"
// conversations use publicKey / URL
}
fun storeConfig(key: String, data: ByteArray) {
val db = writableDatabase
val contentValues = contentValuesOf(
KEY to key,
VALUE to data
)
db.insertOrUpdate(TABLE_NAME, contentValues, KEY_WHERE, arrayOf(key))
}
fun retrieveConfig(key: String): ByteArray? {
val db = readableDatabase
val query = db.query(TABLE_NAME, arrayOf(VALUE), KEY_WHERE, arrayOf(key),null, null, null)
val bytes = query?.use { cursor ->
if (!cursor.moveToFirst()) return null
cursor.getBlobOrNull(cursor.getColumnIndex(VALUE))
}
return bytes
}
}

View File

@ -15,6 +15,7 @@ import org.session.libsignal.utilities.Log;
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.BlindedIdMappingDatabase;
import org.thoughtcrime.securesms.database.ConfigDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.EmojiSearchDatabase;
import org.thoughtcrime.securesms.database.GroupDatabase;
@ -75,9 +76,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int lokiV36 = 57;
private static final int lokiV37 = 58;
private static final int lokiV38 = 59;
private static final int lokiV39 = 60;
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
private static final int DATABASE_VERSION = lokiV38;
private static final int DATABASE_VERSION = lokiV39;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@ -180,6 +182,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.RESET_SEQ_NO); // probably not needed but consistent with all migrations
db.execSQL(EmojiSearchDatabase.CREATE_EMOJI_SEARCH_TABLE_COMMAND);
db.execSQL(ReactionDatabase.CREATE_REACTION_TABLE_COMMAND);
db.execSQL(ConfigDatabase.CREATE_CONFIG_TABLE_COMMAND);
executeStatements(db, SmsDatabase.CREATE_INDEXS);
executeStatements(db, MmsDatabase.CREATE_INDEXS);
@ -414,6 +417,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(EmojiSearchDatabase.CREATE_EMOJI_SEARCH_TABLE_COMMAND);
}
if (oldVersion < lokiV39) {
db.execSQL(ConfigDatabase.CREATE_CONFIG_TABLE_COMMAND);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@ -0,0 +1,60 @@
package org.thoughtcrime.securesms.dependencies
import network.loki.messenger.libsession_util.Contacts
import network.loki.messenger.libsession_util.UserProfile
import org.session.libsession.utilities.ConfigFactoryProtocol
import org.thoughtcrime.securesms.database.ConfigDatabase
class ConfigFactory(private val configDatabase: ConfigDatabase, private val maybeGetUserEdSecretKey: ()->ByteArray?):
ConfigFactoryProtocol {
fun keyPairChanged() { // this should only happen restoring or clearing data
_userConfig?.free()
_contacts?.free()
_userConfig = null
_contacts = null
}
private val userLock = Object()
private var _userConfig: UserProfile? = null
private val contactLock = Object()
private var _contacts: Contacts? = null
override val userConfig: UserProfile? = synchronized(userLock) {
if (_userConfig == null) {
val secretKey = maybeGetUserEdSecretKey() ?: return@synchronized null
val userDump = configDatabase.retrieveConfig(ConfigDatabase.USER_KEY)
_userConfig = if (userDump != null) {
UserProfile.newInstance(secretKey, userDump)
} else {
UserProfile.newInstance(secretKey)
}
}
_userConfig
}
override val contacts: Contacts? = synchronized(contactLock) {
if (_contacts == null) {
val secretKey = maybeGetUserEdSecretKey() ?: return@synchronized null
val contactsDump = configDatabase.retrieveConfig(ConfigDatabase.CONTACTS_KEY)
_contacts = if (contactsDump != null) {
Contacts.newInstance(secretKey, contactsDump)
} else {
Contacts.newInstance(secretKey)
}
}
_contacts
}
override fun saveUserConfigDump() {
val dumped = userConfig?.dump() ?: return
configDatabase.storeConfig(ConfigDatabase.USER_KEY, dumped)
}
override fun saveContactConfigDump() {
val dumped = contacts?.dump() ?: return
configDatabase.storeConfig(ConfigDatabase.CONTACTS_KEY, dumped)
}
}

View File

@ -4,16 +4,14 @@ import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ActivityRetainedScoped
import network.loki.messenger.libsession_util.UserProfile
import org.session.libsignal.utilities.guava.Optional
import dagger.hilt.components.SingletonComponent
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
import org.thoughtcrime.securesms.database.ConfigDatabase
import javax.inject.Singleton
@Module
@InstallIn(ActivityRetainedComponent::class)
@InstallIn(SingletonComponent::class)
object SessionUtilModule {
private fun maybeUserEdSecretKey(context: Context): ByteArray? {
@ -22,12 +20,10 @@ object SessionUtilModule {
}
@Provides
@ActivityRetainedScoped
fun provideUser(@ApplicationContext context: Context, configDatabase: ConfigDatabase): Optional<UserProfile> =
maybeUserEdSecretKey(context)?.let { key ->
// also get the currently stored dump
val instance = UserProfile.newInstance(key)
Optional.of(instance)
} ?: Optional.absent()
@Singleton
fun provideConfigFactory(@ApplicationContext context: Context, configDatabase: ConfigDatabase): ConfigFactory =
ConfigFactory(configDatabase) {
maybeUserEdSecretKey(context)
}
}

View File

@ -25,7 +25,6 @@ import kotlinx.coroutines.withContext
import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityHomeBinding
import network.loki.messenger.databinding.ViewMessageRequestBannerBinding
import network.loki.messenger.libsession_util.UserProfile
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@ -38,7 +37,6 @@ import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.ThreadUtils
import org.session.libsignal.utilities.guava.Optional
import org.session.libsignal.utilities.toHexString
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.MuteDialog
@ -52,6 +50,7 @@ import org.thoughtcrime.securesms.database.MmsSmsDatabase
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.groups.OpenGroupManager
import org.thoughtcrime.securesms.home.search.GlobalSearchAdapter
@ -63,9 +62,15 @@ import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.onboarding.SeedActivity
import org.thoughtcrime.securesms.onboarding.SeedReminderViewDelegate
import org.thoughtcrime.securesms.preferences.SettingsActivity
import org.thoughtcrime.securesms.util.*
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.IP2Country
import org.thoughtcrime.securesms.util.disableClipping
import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.show
import org.thoughtcrime.securesms.util.themeState
import java.io.IOException
import java.util.*
import java.util.Locale
import javax.inject.Inject
@AndroidEntryPoint
@ -83,7 +88,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
@Inject lateinit var recipientDatabase: RecipientDatabase
@Inject lateinit var groupDatabase: GroupDatabase
@Inject lateinit var textSecurePreferences: TextSecurePreferences
@Inject lateinit var userProfile: Optional<UserProfile>
@Inject lateinit var configFactory: ConfigFactory
private val globalSearchViewModel by viewModels<GlobalSearchViewModel>()
private val homeViewModel by viewModels<HomeViewModel>()

View File

@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filter
@ -32,12 +33,19 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragment
import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragmentDelegate
import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
import javax.inject.Inject
@AndroidEntryPoint
class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
@Inject
lateinit var configFactory: ConfigFactory
private lateinit var binding: ActivityLinkDeviceBinding
private val adapter = LinkDeviceActivityAdapter(this)
private var restoreJob: Job? = null
@ -103,6 +111,7 @@ class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDel
val keyPairGenerationResult = KeyPairUtilities.generate(seed)
val x25519KeyPair = keyPairGenerationResult.x25519KeyPair
KeyPairUtilities.store(this@LinkDeviceActivity, seed, keyPairGenerationResult.ed25519KeyPair, x25519KeyPair)
configFactory.keyPairChanged()
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(this@LinkDeviceActivity, registrationID)

View File

@ -11,6 +11,7 @@ import android.text.style.ClickableSpan
import android.text.style.StyleSpan
import android.view.View
import android.widget.Toast
import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityRecoveryPhraseRestoreBinding
import org.session.libsession.utilities.TextSecurePreferences
@ -21,10 +22,17 @@ import org.session.libsignal.utilities.hexEncodedPublicKey
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
import javax.inject.Inject
@AndroidEntryPoint
class RecoveryPhraseRestoreActivity : BaseActionBarActivity() {
@Inject
lateinit var configFactory: ConfigFactory
private lateinit var binding: ActivityRecoveryPhraseRestoreBinding
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?) {
@ -72,6 +80,7 @@ class RecoveryPhraseRestoreActivity : BaseActionBarActivity() {
val keyPairGenerationResult = KeyPairUtilities.generate(seed)
val x25519KeyPair = keyPairGenerationResult.x25519KeyPair
KeyPairUtilities.store(this, seed, keyPairGenerationResult.ed25519KeyPair, x25519KeyPair)
configFactory.keyPairChanged()
val userHexEncodedPublicKey = x25519KeyPair.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(this, registrationID)

View File

@ -16,6 +16,7 @@ import android.text.style.StyleSpan
import android.view.View
import android.widget.Toast
import com.goterl.lazysodium.utils.KeyPair
import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityRegisterBinding
import org.session.libsession.utilities.TextSecurePreferences
@ -24,10 +25,17 @@ import org.session.libsignal.utilities.KeyHelper
import org.session.libsignal.utilities.hexEncodedPublicKey
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
import org.thoughtcrime.securesms.dependencies.ConfigFactory
import org.thoughtcrime.securesms.util.push
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
import javax.inject.Inject
@AndroidEntryPoint
class RegisterActivity : BaseActionBarActivity() {
@Inject
lateinit var configFactory: ConfigFactory
private lateinit var binding: ActivityRegisterBinding
private var seed: ByteArray? = null
private var ed25519KeyPair: KeyPair? = null
@ -110,6 +118,7 @@ class RegisterActivity : BaseActionBarActivity() {
// region Interaction
private fun register() {
KeyPairUtilities.store(this, seed!!, ed25519KeyPair!!, x25519KeyPair!!)
configFactory.keyPairChanged()
val userHexEncodedPublicKey = x25519KeyPair!!.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(this, registrationID)

View File

@ -18,6 +18,7 @@ android {
dependencies {
implementation project(":libsignal")
implementation project(":libsession-util")
implementation project(":liblazysodium")
// implementation 'com.goterl:lazysodium-android:5.0.2@aar'
implementation "net.java.dev.jna:jna:5.8.0@aar"

View File

@ -12,6 +12,7 @@ import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.jobs.MessageReceiveParameters
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.snode.SnodeModule
import org.session.libsession.utilities.ConfigFactoryProtocol
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Snode
import java.security.SecureRandom
@ -20,7 +21,7 @@ import java.util.TimerTask
private class PromiseCanceledException : Exception("Promise canceled.")
class Poller {
class Poller(private val configFactory: ConfigFactoryProtocol) {
var userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey() ?: ""
private var hasStarted: Boolean = false
private val usedSnodes: MutableSet<Snode> = mutableSetOf()

View File

@ -0,0 +1,11 @@
package org.session.libsession.utilities
import network.loki.messenger.libsession_util.Contacts
import network.loki.messenger.libsession_util.UserProfile
interface ConfigFactoryProtocol {
val userConfig: UserProfile?
val contacts: Contacts?
fun saveUserConfigDump()
fun saveContactConfigDump()
}