mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-25 11:05:25 +00:00
feat: add in config storage and injection to common places, managing lifecycle of native instances
This commit is contained in:
parent
0729bdab20
commit
00fe77af8a
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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>()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
}
|
Loading…
Reference in New Issue
Block a user