diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/UpdateCallLinkRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/UpdateCallLinkRepository.kt index 8f2f21cca6..88d04130af 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/UpdateCallLinkRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/UpdateCallLinkRepository.kt @@ -48,18 +48,25 @@ class UpdateCallLinkRepository( .subscribeOn(Schedulers.io()) } - fun revokeCallLink(credentials: CallLinkCredentials): Single { + fun deleteCallLink(credentials: CallLinkCredentials): Single { return callLinkManager - .updateCallLinkRevoked(credentials, true) + .deleteCallLink(credentials) .doOnSuccess(updateState(credentials)) .subscribeOn(Schedulers.io()) } private fun updateState(credentials: CallLinkCredentials): (UpdateCallLinkResult) -> Unit { return { result -> - if (result is UpdateCallLinkResult.Success) { - SignalDatabase.callLinks.updateCallLinkState(credentials.roomId, result.state) - ApplicationDependencies.getJobManager().add(CallLinkUpdateSendJob(credentials.roomId)) + when (result) { + is UpdateCallLinkResult.Update -> { + SignalDatabase.callLinks.updateCallLinkState(credentials.roomId, result.state) + ApplicationDependencies.getJobManager().add(CallLinkUpdateSendJob(credentials.roomId)) + } + is UpdateCallLinkResult.Delete -> { + SignalDatabase.callLinks.markRevoked(credentials.roomId) + ApplicationDependencies.getJobManager().add(CallLinkUpdateSendJob(credentials.roomId)) + } + else -> {} } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt index fe80e42dd6..1be045338f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/create/CreateCallLinkBottomSheetDialogFragment.kt @@ -159,7 +159,7 @@ class CreateCallLinkBottomSheetDialogFragment : ComposeBottomSheetDialogFragment private fun setCallName(callName: String) { lifecycleDisposable += viewModel.setCallName(callName).subscribeBy(onSuccess = { - if (it !is UpdateCallLinkResult.Success) { + if (it !is UpdateCallLinkResult.Update) { Log.w(TAG, "Failed to update call link name") toastFailure() } @@ -168,7 +168,7 @@ class CreateCallLinkBottomSheetDialogFragment : ComposeBottomSheetDialogFragment private fun setApproveAllMembers(approveAllMembers: Boolean) { lifecycleDisposable += viewModel.setApproveAllMembers(approveAllMembers).subscribeBy(onSuccess = { - if (it !is UpdateCallLinkResult.Success) { + if (it !is UpdateCallLinkResult.Update) { Log.w(TAG, "Failed to update call link restrictions") toastFailure() } @@ -177,7 +177,7 @@ class CreateCallLinkBottomSheetDialogFragment : ComposeBottomSheetDialogFragment private fun toggleApproveAllMembers() { lifecycleDisposable += viewModel.toggleApproveAllMembers().subscribeBy(onSuccess = { - if (it !is UpdateCallLinkResult.Success) { + if (it !is UpdateCallLinkResult.Update) { Log.w(TAG, "Failed to update call link restrictions") toastFailure() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt index 96063b53e9..f0689ac88a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsFragment.kt @@ -141,9 +141,9 @@ class CallLinkDetailsFragment : ComposeFragment(), CallLinkDetailsCallback { override fun onDeleteConfirmed() { viewModel.setDisplayRevocationDialog(false) - lifecycleDisposable += viewModel.revoke().observeOn(AndroidSchedulers.mainThread()).subscribeBy(onSuccess = { + lifecycleDisposable += viewModel.delete().observeOn(AndroidSchedulers.mainThread()).subscribeBy(onSuccess = { when (it) { - is UpdateCallLinkResult.Success -> ActivityCompat.finishAfterTransition(requireActivity()) + is UpdateCallLinkResult.Update -> ActivityCompat.finishAfterTransition(requireActivity()) else -> { Log.w(TAG, "Failed to revoke. $it") toastFailure() @@ -158,7 +158,7 @@ class CallLinkDetailsFragment : ComposeFragment(), CallLinkDetailsCallback { override fun onApproveAllMembersChanged(checked: Boolean) { lifecycleDisposable += viewModel.setApproveAllMembers(checked).observeOn(AndroidSchedulers.mainThread()).subscribeBy(onSuccess = { - if (it !is UpdateCallLinkResult.Success) { + if (it !is UpdateCallLinkResult.Update) { Log.w(TAG, "Failed to change restrictions. $it") toastFailure() } @@ -167,7 +167,7 @@ class CallLinkDetailsFragment : ComposeFragment(), CallLinkDetailsCallback { private fun setName(name: String) { lifecycleDisposable += viewModel.setName(name).observeOn(AndroidSchedulers.mainThread()).subscribeBy(onSuccess = { - if (it !is UpdateCallLinkResult.Success) { + if (it !is UpdateCallLinkResult.Update) { Log.w(TAG, "Failed to set name. $it") toastFailure() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsViewModel.kt index 9bfdf9f1bc..877a8186c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/details/CallLinkDetailsViewModel.kt @@ -71,9 +71,9 @@ class CallLinkDetailsViewModel( return mutationRepository.setCallName(credentials, name) } - fun revoke(): Single { + fun delete(): Single { val credentials = _state.value.callLink?.credentials ?: error("User cannot change the name of this call.") - return mutationRepository.revokeCallLink(credentials) + return mutationRepository.deleteCallLink(credentials) } class Factory(private val callLinkRoomId: CallLinkRoomId) : ViewModelProvider.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogRepository.kt index a2ab894fd5..e3c0f5ae7c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogRepository.kt @@ -101,7 +101,7 @@ class CallLogRepository( } SignalDatabase.callLinks.getAllAdminCallLinksExcept(emptySet()) - }.flatMap(this::revokeAndCollectResults).map { 0 }.subscribeOn(Schedulers.io()) + }.flatMap(this::deleteAndCollectResults).map { 0 }.subscribeOn(Schedulers.io()) } /** @@ -117,7 +117,7 @@ class CallLogRepository( val allCallLinkIds = SignalDatabase.calls.getCallLinkRoomIdsFromCallRowIds(selectedCallRowIds) + selectedRoomIds SignalDatabase.callLinks.deleteNonAdminCallLinks(allCallLinkIds) SignalDatabase.callLinks.getAdminCallLinks(allCallLinkIds) - }.flatMap(this::revokeAndCollectResults).subscribeOn(Schedulers.io()) + }.flatMap(this::deleteAndCollectResults).subscribeOn(Schedulers.io()) } /** @@ -133,16 +133,16 @@ class CallLogRepository( val allCallLinkIds = SignalDatabase.calls.getCallLinkRoomIdsFromCallRowIds(selectedCallRowIds) + selectedRoomIds SignalDatabase.callLinks.deleteAllNonAdminCallLinksExcept(allCallLinkIds) SignalDatabase.callLinks.getAllAdminCallLinksExcept(allCallLinkIds) - }.flatMap(this::revokeAndCollectResults).subscribeOn(Schedulers.io()) + }.flatMap(this::deleteAndCollectResults).subscribeOn(Schedulers.io()) } - private fun revokeAndCollectResults(callLinksToRevoke: Set): Single { + private fun deleteAndCollectResults(callLinksToRevoke: Set): Single { return Single.merge( callLinksToRevoke.map { - updateCallLinkRepository.revokeCallLink(it.credentials!!) + updateCallLinkRepository.deleteCallLink(it.credentials!!) } ).reduce(0) { acc, current -> - acc + (if (current is UpdateCallLinkResult.Success) 0 else 1) + acc + (if (current is UpdateCallLinkResult.Update) 0 else 1) }.doOnTerminate { SignalDatabase.calls.updateAdHocCallEventDeletionTimestamps() }.doOnDispose { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt index a79bad8fa6..0bb0225e25 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt @@ -393,7 +393,7 @@ class ControlsAndInfoController( controlsAndInfoViewModel.setApproveAllMembers(checked) .observeOn(AndroidSchedulers.mainThread()) .subscribeBy(onSuccess = { - if (it !is UpdateCallLinkResult.Success) { + if (it !is UpdateCallLinkResult.Update) { Log.w(TAG, "Failed to change restrictions. $it") toastFailure() } @@ -419,7 +419,7 @@ class ControlsAndInfoController( .observeOn(AndroidSchedulers.mainThread()) .subscribeBy( onSuccess = { - if (it !is UpdateCallLinkResult.Success) { + if (it !is UpdateCallLinkResult.Update) { Log.w(TAG, "Failed to set name. $it") toastFailure() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt index 2b9599f787..1d1f685622 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallLinkTable.kt @@ -231,6 +231,37 @@ class CallLinkTable(context: Context, databaseHelper: SignalDatabase) : Database } } + /** + * Puts the call link into the "revoked" state which will hide it from the UI and + * delete it after a few days. + */ + fun markRevoked( + roomId: CallLinkRoomId + ) { + writableDatabase.withinTransaction { db -> + db.update(TABLE_NAME) + .values("$REVOKED" to true) + .where("$ROOM_ID", roomId) + .run() + + SignalDatabase.calls.updateAdHocCallEventDeletionTimestamps() + } + } + + /** + * Deletes the call link. This should only happen *after* we send out a sync message + * or receive a sync message which deletes the corresponding link. + */ + fun deleteCallLink( + roomId: CallLinkRoomId + ) { + writableDatabase.withinTransaction { db -> + db.delete(TABLE_NAME) + .where("$ROOM_ID", roomId) + .run() + } + } + fun deleteNonAdminCallLinks(roomIds: Set) { val queries = SqlUtil.buildCollectionQuery(ROOM_ID, roomIds) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt index 6abe96571f..3a37011999 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt @@ -26,7 +26,8 @@ import java.util.concurrent.TimeUnit */ class CallLinkUpdateSendJob private constructor( parameters: Parameters, - private val callLinkRoomId: CallLinkRoomId + private val callLinkRoomId: CallLinkRoomId, + private val callLinkUpdateType: CallLinkUpdate.Type ) : BaseJob(parameters) { companion object { @@ -35,7 +36,8 @@ class CallLinkUpdateSendJob private constructor( } constructor( - callLinkRoomId: CallLinkRoomId + callLinkRoomId: CallLinkRoomId, + callLinkUpdateType: CallLinkUpdate.Type = CallLinkUpdate.Type.UPDATE ) : this( Parameters.Builder() .setQueue("CallLinkUpdateSendJob") @@ -43,11 +45,18 @@ class CallLinkUpdateSendJob private constructor( .setMaxAttempts(Parameters.UNLIMITED) .addConstraint(NetworkConstraint.KEY) .build(), - callLinkRoomId + callLinkRoomId, + callLinkUpdateType ) override fun serialize(): ByteArray = CallLinkUpdateSendJobData.Builder() .callLinkRoomId(callLinkRoomId.serialize()) + .type( + when (callLinkUpdateType) { + CallLinkUpdate.Type.UPDATE -> CallLinkUpdateSendJobData.Type.UPDATE + CallLinkUpdate.Type.DELETE -> CallLinkUpdateSendJobData.Type.DELETE + } + ) .build() .encode() @@ -67,10 +76,17 @@ class CallLinkUpdateSendJob private constructor( return } - val callLinkUpdate = CallLinkUpdate(rootKey = callLink.credentials.linkKeyBytes.toByteString()) + val callLinkUpdate = CallLinkUpdate( + rootKey = callLink.credentials.linkKeyBytes.toByteString(), + type = callLinkUpdateType + ) ApplicationDependencies.getSignalServiceMessageSender() .sendSyncMessage(SignalServiceSyncMessage.forCallLinkUpdate(callLinkUpdate), Optional.empty()) + + if (callLinkUpdateType == CallLinkUpdate.Type.DELETE) { + SignalDatabase.callLinks.deleteCallLink(callLinkRoomId) + } } override fun onShouldRetry(e: Exception): Boolean { @@ -83,9 +99,16 @@ class CallLinkUpdateSendJob private constructor( class Factory : Job.Factory { override fun create(parameters: Parameters, serializedData: ByteArray?): CallLinkUpdateSendJob { + val jobData = CallLinkUpdateSendJobData.ADAPTER.decode(serializedData!!) + val type: CallLinkUpdate.Type = when (jobData.type) { + CallLinkUpdateSendJobData.Type.UPDATE, null -> CallLinkUpdate.Type.UPDATE + CallLinkUpdateSendJobData.Type.DELETE -> CallLinkUpdate.Type.DELETE + } + return CallLinkUpdateSendJob( parameters, - CallLinkRoomId.DatabaseSerializer.deserialize(CallLinkUpdateSendJobData.ADAPTER.decode(serializedData!!).callLinkRoomId) + CallLinkRoomId.DatabaseSerializer.deserialize(jobData.callLinkRoomId), + type ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt index d5495d7846..ed57963347 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt @@ -1223,6 +1223,13 @@ object SyncMessageProcessor { } val roomId = CallLinkRoomId.fromCallLinkRootKey(callLinkRootKey) + if (callLinkUpdate.type == CallLinkUpdate.Type.DELETE) { + log(envelopeTimestamp, "Synchronize call link deletion.") + SignalDatabase.callLinks.deleteCallLink(roomId) + + return + } + if (SignalDatabase.callLinks.callLinkExists(roomId)) { log(envelopeTimestamp, "Synchronize call link for a link we already know about. Updating credentials.") SignalDatabase.callLinks.updateCallLinkCredentials( diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/SignalCallLinkManager.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/SignalCallLinkManager.kt index 3bc60df89b..0b1e4ef928 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/SignalCallLinkManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/SignalCallLinkManager.kt @@ -171,7 +171,7 @@ class SignalCallLinkManager( name ) { result -> if (result.isSuccess) { - emitter.onSuccess(UpdateCallLinkResult.Success(result.value!!.toAppState())) + emitter.onSuccess(UpdateCallLinkResult.Update(result.value!!.toAppState())) } else { emitter.onSuccess(UpdateCallLinkResult.Failure(result.status)) } @@ -198,7 +198,7 @@ class SignalCallLinkManager( restrictions ) { result -> if (result.isSuccess) { - emitter.onSuccess(UpdateCallLinkResult.Success(result.value!!.toAppState())) + emitter.onSuccess(UpdateCallLinkResult.Update(result.value!!.toAppState())) } else { emitter.onSuccess(UpdateCallLinkResult.Failure(result.status)) } @@ -206,9 +206,8 @@ class SignalCallLinkManager( } } - fun updateCallLinkRevoked( - credentials: CallLinkCredentials, - revoked: Boolean + fun deleteCallLink( + credentials: CallLinkCredentials ): Single { if (credentials.adminPassBytes == null) { return Single.just(UpdateCallLinkResult.NotAuthorized) @@ -217,15 +216,14 @@ class SignalCallLinkManager( return Single.create { emitter -> val credentialPresentation = requestCallLinkAuthCredentialPresentation(credentials.linkKeyBytes) - callManager.updateCallLinkRevoked( + callManager.deleteCallLink( SignalStore.internalValues().groupCallingServer(), credentialPresentation.serialize(), CallLinkRootKey(credentials.linkKeyBytes), - credentials.adminPassBytes, - revoked + credentials.adminPassBytes ) { result -> - if (result.isSuccess) { - emitter.onSuccess(UpdateCallLinkResult.Success(result.value!!.toAppState())) + if (result.isSuccess && result.value == true) { + emitter.onSuccess(UpdateCallLinkResult.Delete(credentials.roomId)) } else { emitter.onSuccess(UpdateCallLinkResult.Failure(result.status)) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/UpdateCallLinkResult.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/UpdateCallLinkResult.kt index c90033f282..2d72d138b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/UpdateCallLinkResult.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/UpdateCallLinkResult.kt @@ -9,10 +9,14 @@ package org.thoughtcrime.securesms.service.webrtc.links * Result type for call link updates. */ sealed interface UpdateCallLinkResult { - data class Success( + data class Update( val state: SignalCallLinkState ) : UpdateCallLinkResult + data class Delete( + val roomId: CallLinkRoomId + ) : UpdateCallLinkResult + data class Failure( val status: Short ) : UpdateCallLinkResult diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index 89823e7a9c..7e2f51847d 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -29,7 +29,13 @@ message CallLogEventSendJobData { } message CallLinkUpdateSendJobData { - string callLinkRoomId = 1; + enum Type { + UPDATE = 0; + DELETE = 1; + } + + string callLinkRoomId = 1; + optional Type type = 2; } message AttachmentUploadJobData { diff --git a/dependencies.gradle.kts b/dependencies.gradle.kts index 8f9ba88529..ddf3ffd464 100644 --- a/dependencies.gradle.kts +++ b/dependencies.gradle.kts @@ -119,7 +119,7 @@ dependencyResolutionManagement { library("libsignal-client", "org.signal", "libsignal-client").versionRef("libsignal-client") library("libsignal-android", "org.signal", "libsignal-android").versionRef("libsignal-client") library("signal-aesgcmprovider", "org.signal:aesgcmprovider:0.0.3") - library("signal-ringrtc", "org.signal:ringrtc-android:2.38.0") + library("signal-ringrtc", "org.signal:ringrtc-android:2.39.0") library("signal-android-database-sqlcipher", "org.signal:sqlcipher-android:4.5.4-S2") // Third Party diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 1e254eeca9..03adf6c051 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5721,12 +5721,12 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + - - + + diff --git a/libsignal-service/src/main/protowire/SignalService.proto b/libsignal-service/src/main/protowire/SignalService.proto index 2de3e6ac75..fe19d3efb1 100644 --- a/libsignal-service/src/main/protowire/SignalService.proto +++ b/libsignal-service/src/main/protowire/SignalService.proto @@ -621,8 +621,14 @@ message SyncMessage { } message CallLinkUpdate { + enum Type { + UPDATE = 0; + DELETE = 1; + } + optional bytes rootKey = 1; optional bytes adminPassKey = 2; + optional Type type = 3; } message CallLogEvent {