diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/storage/InternalStorageServicePlaygroundViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/storage/InternalStorageServicePlaygroundViewModel.kt index 85c34fbf51..f8dc7f652a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/storage/InternalStorageServicePlaygroundViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/storage/InternalStorageServicePlaygroundViewModel.kt @@ -16,11 +16,11 @@ import kotlinx.coroutines.withContext import org.signal.core.util.ByteSize import org.signal.core.util.bytes import org.signal.core.util.logging.Log +import org.signal.network.service.StorageServiceService import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.whispersystems.signalservice.api.storage.SignalStorageManifest import org.whispersystems.signalservice.api.storage.SignalStorageRecord -import org.whispersystems.signalservice.api.storage.StorageServiceRepository class InternalStorageServicePlaygroundViewModel : ViewModel() { @@ -47,12 +47,12 @@ class InternalStorageServicePlaygroundViewModel : ViewModel() { fun onViewTabSelected() { viewModelScope.launch { withContext(Dispatchers.IO) { - val repository = StorageServiceRepository(AppDependencies.storageServiceApi) + val repository = StorageServiceService(AppDependencies.storageServiceApi) val storageKey = SignalStore.storageService.storageKeyForInitialDataRestore ?: SignalStore.storageService.storageKey val manifest = when (val result = repository.getStorageManifest(storageKey)) { - is StorageServiceRepository.ManifestResult.Success -> result.manifest - is StorageServiceRepository.ManifestResult.NotFoundError -> { + is StorageServiceService.ManifestResult.Success -> result.manifest + is StorageServiceService.ManifestResult.NotFoundError -> { Log.w(TAG, "Manifest not found!") _oneOffEvents.value = OneOffEvent.ManifestNotFoundError return@withContext @@ -66,7 +66,7 @@ class InternalStorageServicePlaygroundViewModel : ViewModel() { _manifest.value = manifest val records = when (val result = repository.readStorageRecords(storageKey, manifest.recordIkm, manifest.storageIds)) { - is StorageServiceRepository.StorageRecordResult.Success -> result.records + is StorageServiceService.StorageRecordResult.Success -> result.records else -> { Log.w(TAG, "Failed to fetch records!") _oneOffEvents.value = OneOffEvent.StorageRecordDecryptionError diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.kt index c80a3bf53f..d52643f9e6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageAccountRestoreJob.kt @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.jobs import org.signal.core.util.logging.Log +import org.signal.network.service.StorageServiceService +import org.signal.network.service.StorageServiceService.ManifestResult import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.Job @@ -14,8 +16,6 @@ import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException import org.whispersystems.signalservice.api.storage.SignalAccountRecord import org.whispersystems.signalservice.api.storage.SignalStorageManifest import org.whispersystems.signalservice.api.storage.SignalStorageRecord -import org.whispersystems.signalservice.api.storage.StorageServiceRepository -import org.whispersystems.signalservice.api.storage.StorageServiceRepository.ManifestResult import java.util.concurrent.TimeUnit /** @@ -56,7 +56,7 @@ class StorageAccountRestoreJob private constructor(parameters: Parameters) : Bas SignalStore.storageService.storageKey } - val repository = StorageServiceRepository(SignalNetwork.storageService) + val repository = StorageServiceService(SignalNetwork.storageService) Log.i(TAG, "Retrieving manifest...") val manifest: SignalStorageManifest? = when (val result = repository.getStorageManifest(storageServiceKey)) { @@ -85,14 +85,14 @@ class StorageAccountRestoreJob private constructor(parameters: Parameters) : Bas Log.i(TAG, "Retrieving account record...") val records: List = when (val result = repository.readStorageRecords(storageServiceKey, manifest.recordIkm, listOf(accountId.get()))) { - is StorageServiceRepository.StorageRecordResult.Success -> result.records - is StorageServiceRepository.StorageRecordResult.DecryptionError -> { + is StorageServiceService.StorageRecordResult.Success -> result.records + is StorageServiceService.StorageRecordResult.DecryptionError -> { Log.w(TAG, "Account record was undecryptable. Not restoring. Force-pushing.") AppDependencies.jobManager.add(StorageForcePushJob()) return } - is StorageServiceRepository.StorageRecordResult.NetworkError -> throw result.exception - is StorageServiceRepository.StorageRecordResult.StatusCodeError -> throw result.exception + is StorageServiceService.StorageRecordResult.NetworkError -> throw result.exception + is StorageServiceService.StorageRecordResult.StatusCodeError -> throw result.exception } val record = if (records.isNotEmpty()) records[0] else null diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.kt index 732b80de6e..68d651c693 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.jobs import org.signal.core.util.SqlUtil import org.signal.core.util.logging.Log import org.signal.core.util.logging.logI +import org.signal.network.service.StorageServiceService import org.thoughtcrime.securesms.components.settings.app.chats.folders.ChatFolderId import org.thoughtcrime.securesms.database.ChatFolderTables.ChatFolderTable import org.thoughtcrime.securesms.database.NotificationProfileTables @@ -25,7 +26,6 @@ import org.whispersystems.signalservice.api.storage.RecordIkm import org.whispersystems.signalservice.api.storage.SignalStorageManifest import org.whispersystems.signalservice.api.storage.SignalStorageRecord import org.whispersystems.signalservice.api.storage.StorageId -import org.whispersystems.signalservice.api.storage.StorageServiceRepository import java.io.IOException import java.util.Collections import java.util.concurrent.TimeUnit @@ -71,7 +71,7 @@ class StorageForcePushJob private constructor(parameters: Parameters) : BaseJob( } val storageServiceKey = SignalStore.storageService.storageKey - val repository = StorageServiceRepository(AppDependencies.storageServiceApi) + val repository = StorageServiceService(AppDependencies.storageServiceApi) val currentVersion: Long = when (val result = repository.getManifestVersion()) { is NetworkResult.Success -> result.result @@ -133,10 +133,10 @@ class StorageForcePushJob private constructor(parameters: Parameters) : BaseJob( if (newVersion > 1) { Log.i(TAG, "Force-pushing data. Inserting ${inserts.size} IDs.") when (val result = repository.resetAndWriteStorageRecords(storageServiceKey, manifest, inserts)) { - StorageServiceRepository.WriteStorageRecordsResult.Success -> Unit - is StorageServiceRepository.WriteStorageRecordsResult.StatusCodeError -> throw result.exception - is StorageServiceRepository.WriteStorageRecordsResult.NetworkError -> throw result.exception - StorageServiceRepository.WriteStorageRecordsResult.ConflictError -> { + StorageServiceService.WriteStorageRecordsResult.Success -> Unit + is StorageServiceService.WriteStorageRecordsResult.StatusCodeError -> throw result.exception + is StorageServiceService.WriteStorageRecordsResult.NetworkError -> throw result.exception + StorageServiceService.WriteStorageRecordsResult.ConflictError -> { Log.w(TAG, "Hit a conflict. Trying again.") throw RetryLaterException() } @@ -144,10 +144,10 @@ class StorageForcePushJob private constructor(parameters: Parameters) : BaseJob( } else { Log.i(TAG, "First version, normal push. Inserting ${inserts.size} IDs.") when (val result = repository.writeStorageRecords(storageServiceKey, manifest, inserts, emptyList())) { - StorageServiceRepository.WriteStorageRecordsResult.Success -> Unit - is StorageServiceRepository.WriteStorageRecordsResult.StatusCodeError -> throw result.exception - is StorageServiceRepository.WriteStorageRecordsResult.NetworkError -> throw result.exception - is StorageServiceRepository.WriteStorageRecordsResult.ConflictError -> { + StorageServiceService.WriteStorageRecordsResult.Success -> Unit + is StorageServiceService.WriteStorageRecordsResult.StatusCodeError -> throw result.exception + is StorageServiceService.WriteStorageRecordsResult.NetworkError -> throw result.exception + is StorageServiceService.WriteStorageRecordsResult.ConflictError -> { Log.w(TAG, "Hit a conflict. Trying again.") throw RetryLaterException() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageRotateManifestJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageRotateManifestJob.kt index 3baf182d59..fdb4b1d277 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageRotateManifestJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageRotateManifestJob.kt @@ -2,12 +2,12 @@ package org.thoughtcrime.securesms.jobs import org.signal.core.models.storageservice.StorageKey import org.signal.core.util.logging.Log +import org.signal.network.service.StorageServiceService import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint import org.thoughtcrime.securesms.keyvalue.SignalStore import org.whispersystems.signalservice.api.storage.SignalStorageManifest -import org.whispersystems.signalservice.api.storage.StorageServiceRepository import java.util.concurrent.TimeUnit /** @@ -52,27 +52,27 @@ class StorageRotateManifestJob private constructor(parameters: Parameters) : Job } val storageServiceKey = SignalStore.storageService.storageKey - val repository = StorageServiceRepository(AppDependencies.storageServiceApi) + val repository = StorageServiceService(AppDependencies.storageServiceApi) val currentManifest: SignalStorageManifest = when (val result = repository.getStorageManifest(restoreKey)) { - is StorageServiceRepository.ManifestResult.Success -> { + is StorageServiceService.ManifestResult.Success -> { result.manifest } - is StorageServiceRepository.ManifestResult.DecryptionError -> { + is StorageServiceService.ManifestResult.DecryptionError -> { Log.w(TAG, "Failed to decrypt the manifest! Only recourse is to force push.", result.exception) AppDependencies.jobManager.add(StorageForcePushJob()) return Result.failure() } - is StorageServiceRepository.ManifestResult.NetworkError -> { + is StorageServiceService.ManifestResult.NetworkError -> { Log.w(TAG, "Encountered a network error during read, retrying.", result.exception) return Result.retry(defaultBackoff()) } - StorageServiceRepository.ManifestResult.NotFoundError -> { + StorageServiceService.ManifestResult.NotFoundError -> { Log.w(TAG, "No existing manifest was found! Force pushing.") AppDependencies.jobManager.add(StorageForcePushJob()) return Result.failure() } - is StorageServiceRepository.ManifestResult.StatusCodeError -> { + is StorageServiceService.ManifestResult.StatusCodeError -> { Log.w(TAG, "Encountered a status code error during read, retrying.", result.exception) return Result.retry(defaultBackoff()) } @@ -87,7 +87,7 @@ class StorageRotateManifestJob private constructor(parameters: Parameters) : Job val manifestWithNewVersion = currentManifest.copy(version = currentManifest.version + 1) return when (val result = repository.writeUnchangedManifest(storageServiceKey, manifestWithNewVersion)) { - StorageServiceRepository.WriteStorageRecordsResult.Success -> { + StorageServiceService.WriteStorageRecordsResult.Success -> { Log.i(TAG, "Successfully rotated the manifest as version ${manifestWithNewVersion.version}.${manifestWithNewVersion.sourceDeviceId}. Clearing restore key.") SignalStore.svr.masterKeyForInitialDataRestore = null @@ -96,18 +96,18 @@ class StorageRotateManifestJob private constructor(parameters: Parameters) : Job Result.success() } - StorageServiceRepository.WriteStorageRecordsResult.ConflictError -> { + StorageServiceService.WriteStorageRecordsResult.ConflictError -> { Log.w(TAG, "Hit a conflict! Enqueuing a sync followed by another rotation.") AppDependencies.jobManager.add(StorageSyncJob.forRemoteChange()) AppDependencies.jobManager.add(StorageRotateManifestJob()) Result.failure() } - is StorageServiceRepository.WriteStorageRecordsResult.StatusCodeError -> { + is StorageServiceService.WriteStorageRecordsResult.StatusCodeError -> { Log.w(TAG, "Encountered a non-conflict status code error during write. Failing.", result.exception) Result.failure() } - is StorageServiceRepository.WriteStorageRecordsResult.NetworkError -> { + is StorageServiceService.WriteStorageRecordsResult.NetworkError -> { Log.w(TAG, "Encountered a network error during write, retrying.", result.exception) Result.retry(defaultBackoff()) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt index 32403193ac..42f6b03814 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.kt @@ -8,6 +8,8 @@ import org.signal.core.util.Stopwatch import org.signal.core.util.logging.Log import org.signal.core.util.withinTransaction import org.signal.libsignal.protocol.InvalidKeyException +import org.signal.network.service.StorageServiceService +import org.signal.network.service.StorageServiceService.ManifestIfDifferentVersionResult import org.thoughtcrime.securesms.database.ChatFolderTables.ChatFolderTable import org.thoughtcrime.securesms.database.NotificationProfileTables import org.thoughtcrime.securesms.database.RecipientTable @@ -47,8 +49,6 @@ import org.whispersystems.signalservice.api.storage.SignalStorageManifest import org.whispersystems.signalservice.api.storage.SignalStorageRecord import org.whispersystems.signalservice.api.storage.SignalStoryDistributionListRecord import org.whispersystems.signalservice.api.storage.StorageId -import org.whispersystems.signalservice.api.storage.StorageServiceRepository -import org.whispersystems.signalservice.api.storage.StorageServiceRepository.ManifestIfDifferentVersionResult import org.whispersystems.signalservice.api.storage.toSignalAccountRecord import org.whispersystems.signalservice.api.storage.toSignalCallLinkRecord import org.whispersystems.signalservice.api.storage.toSignalChatFolderRecord @@ -247,7 +247,7 @@ class StorageSyncJob private constructor(parameters: Parameters, private var loc private fun performSync(storageServiceKey: StorageKey): Boolean { val stopwatch = Stopwatch("StorageSync") val db = SignalDatabase.rawDatabase - val repository = StorageServiceRepository(SignalNetwork.storageService) + val repository = StorageServiceService(SignalNetwork.storageService) val localManifest = SignalStore.storageService.manifest val remoteManifest = if (localManifestOutOfDate || localManifest.version < 1 || runAttempt >= 3) { @@ -309,10 +309,10 @@ class StorageSyncJob private constructor(parameters: Parameters, private var loc Log.i(TAG, "[Remote Sync] Retrieving records for key difference.") val remoteOnlyRecords = when (val result = repository.readStorageRecords(storageServiceKey, remoteManifest.recordIkm, idDifference.remoteOnlyIds)) { - is StorageServiceRepository.StorageRecordResult.Success -> result.records - is StorageServiceRepository.StorageRecordResult.DecryptionError -> throw result.exception - is StorageServiceRepository.StorageRecordResult.NetworkError -> throw result.exception - is StorageServiceRepository.StorageRecordResult.StatusCodeError -> throw result.exception + is StorageServiceService.StorageRecordResult.Success -> result.records + is StorageServiceService.StorageRecordResult.DecryptionError -> throw result.exception + is StorageServiceService.StorageRecordResult.NetworkError -> throw result.exception + is StorageServiceService.StorageRecordResult.StatusCodeError -> throw result.exception } stopwatch.split("remote-records") @@ -400,10 +400,10 @@ class StorageSyncJob private constructor(parameters: Parameters, private var loc StorageSyncValidations.validate(remoteWriteOperation, remoteManifest, needsForcePush, self) when (val result = repository.writeStorageRecords(storageServiceKey, remoteWriteOperation.manifest, remoteWriteOperation.inserts, remoteWriteOperation.deletes)) { - StorageServiceRepository.WriteStorageRecordsResult.Success -> Unit - is StorageServiceRepository.WriteStorageRecordsResult.StatusCodeError -> throw result.exception - is StorageServiceRepository.WriteStorageRecordsResult.NetworkError -> throw result.exception - StorageServiceRepository.WriteStorageRecordsResult.ConflictError -> { + StorageServiceService.WriteStorageRecordsResult.Success -> Unit + is StorageServiceService.WriteStorageRecordsResult.StatusCodeError -> throw result.exception + is StorageServiceService.WriteStorageRecordsResult.NetworkError -> throw result.exception + StorageServiceService.WriteStorageRecordsResult.ConflictError -> { Log.w(TAG, "Hit a conflict when trying to resolve the conflict! Retrying.") localManifestOutOfDate = true throw RetryLaterException() @@ -428,10 +428,10 @@ class StorageSyncJob private constructor(parameters: Parameters, private var loc Log.i(TAG, "We have ${knownUnknownIds.size} unknown records that we can now process.") val remote = when (val result = repository.readStorageRecords(storageServiceKey, remoteManifest.recordIkm, knownUnknownIds)) { - is StorageServiceRepository.StorageRecordResult.Success -> result.records - is StorageServiceRepository.StorageRecordResult.DecryptionError -> throw result.exception - is StorageServiceRepository.StorageRecordResult.NetworkError -> throw result.exception - is StorageServiceRepository.StorageRecordResult.StatusCodeError -> throw result.exception + is StorageServiceService.StorageRecordResult.Success -> result.records + is StorageServiceService.StorageRecordResult.DecryptionError -> throw result.exception + is StorageServiceService.StorageRecordResult.NetworkError -> throw result.exception + is StorageServiceService.StorageRecordResult.StatusCodeError -> throw result.exception } val records = StorageRecordCollection(remote) diff --git a/app/src/spinner/java/org/thoughtcrime/securesms/StorageServicePlugin.kt b/app/src/spinner/java/org/thoughtcrime/securesms/StorageServicePlugin.kt index 926be3ed72..3749de5237 100644 --- a/app/src/spinner/java/org/thoughtcrime/securesms/StorageServicePlugin.kt +++ b/app/src/spinner/java/org/thoughtcrime/securesms/StorageServicePlugin.kt @@ -1,11 +1,11 @@ package org.thoughtcrime.securesms import org.signal.core.util.Base64 +import org.signal.network.service.StorageServiceService import org.signal.spinner.Plugin import org.signal.spinner.PluginResult import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.net.SignalNetwork -import org.whispersystems.signalservice.api.storage.StorageServiceRepository class StorageServicePlugin : Plugin { override val name: String = "Storage" @@ -15,16 +15,16 @@ class StorageServicePlugin : Plugin { val columns = listOf("Type", "Id", "Data") val rows = mutableListOf>() - val repository = StorageServiceRepository(SignalNetwork.storageService) + val repository = StorageServiceService(SignalNetwork.storageService) val storageServiceKey = SignalStore.storageService.storageKey val manifest = when (val result = repository.getStorageManifest(storageServiceKey)) { - is StorageServiceRepository.ManifestResult.Success -> result.manifest + is StorageServiceService.ManifestResult.Success -> result.manifest else -> return PluginResult.StringResult("Failed to find manifest!") } val signalStorageRecords = when (val result = repository.readStorageRecords(storageServiceKey, manifest.recordIkm, manifest.storageIds)) { - is StorageServiceRepository.StorageRecordResult.Success -> result.records + is StorageServiceService.StorageRecordResult.Success -> result.records else -> return PluginResult.StringResult("Failed to read records!") } diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageServiceRepository.kt b/lib/network/src/main/java/org/signal/network/service/StorageServiceService.kt similarity index 95% rename from lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageServiceRepository.kt rename to lib/network/src/main/java/org/signal/network/service/StorageServiceService.kt index 29889f3734..07063f0f8a 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageServiceRepository.kt +++ b/lib/network/src/main/java/org/signal/network/service/StorageServiceService.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -package org.whispersystems.signalservice.api.storage +package org.signal.network.service import com.squareup.wire.FieldEncoding import okio.ByteString @@ -16,6 +16,13 @@ import org.signal.core.util.logging.Log import org.signal.libsignal.protocol.InvalidKeyException import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException +import org.whispersystems.signalservice.api.storage.RecordIkm +import org.whispersystems.signalservice.api.storage.SignalStorageCipher +import org.whispersystems.signalservice.api.storage.SignalStorageManifest +import org.whispersystems.signalservice.api.storage.SignalStorageRecord +import org.whispersystems.signalservice.api.storage.StorageId +import org.whispersystems.signalservice.api.storage.StorageServiceApi +import org.whispersystems.signalservice.api.storage.typeValue import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord import org.whispersystems.signalservice.internal.storage.protos.ReadOperation import org.whispersystems.signalservice.internal.storage.protos.StorageItem @@ -29,10 +36,10 @@ import java.lang.Exception * Collection of higher-level storage service operations. Each method tends to make multiple * calls to [StorageServiceApi], wrapping the responses in easier-to-use result types. */ -class StorageServiceRepository(private val storageServiceApi: StorageServiceApi) { +class StorageServiceService(private val storageServiceApi: StorageServiceApi) { companion object { - private val TAG = Log.tag(StorageServiceRepository::class) + private val TAG = Log.tag(StorageServiceService::class) private const val STORAGE_READ_MAX_ITEMS: Int = 1000 }