diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index d1867e7ef3..fb371188b3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob; import org.thoughtcrime.securesms.jobs.ExternalLaunchDonationJob; import org.thoughtcrime.securesms.jobs.FcmRefreshJob; import org.thoughtcrime.securesms.jobs.FontDownloaderJob; +import org.thoughtcrime.securesms.jobs.GroupRingCleanupJob; import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob; import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.PnpInitializeDevicesJob; @@ -214,6 +215,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr .addPostRender(() -> ApplicationDependencies.getExoPlayerPool().getPoolStats().getMaxUnreserved()) .addPostRender(() -> ApplicationDependencies.getRecipientCache().warmUp()) .addPostRender(AccountConsistencyWorkerJob::enqueueIfNecessary) + .addPostRender(GroupRingCleanupJob::enqueue) .execute(); Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt index 257b42c5a5..195e925121 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/CallTable.kt @@ -1172,6 +1172,25 @@ class CallTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTabl ) } + fun getLatestRingingCalls(): List { + return readableDatabase.select() + .from(TABLE_NAME) + .where("$EVENT = ?", Event.serialize(Event.RINGING)) + .limit(10) + .orderBy(TIMESTAMP) + .run() + .readToList { + Call.deserialize(it) + } + } + + fun markRingingCallsAsMissed() { + writableDatabase.update(TABLE_NAME) + .values(EVENT to Event.serialize(Event.MISSED)) + .where("$EVENT = ?", Event.serialize(Event.RINGING)) + .run() + } + fun getCallsCount(searchTerm: String?, filter: CallLogFilter): Int { return getCallsCursor(true, 0, 0, searchTerm, filter).use { it.moveToFirst() diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupRingCleanupJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupRingCleanupJob.kt new file mode 100644 index 0000000000..5f6139e458 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupRingCleanupJob.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import java.util.concurrent.TimeUnit + +/** + * Cleans database of stale group rings which can occur if the device or application + * crashes while an incoming ring is happening. + */ +class GroupRingCleanupJob private constructor(parameters: Parameters) : BaseJob(parameters) { + companion object { + const val KEY = "GroupRingCleanupJob" + + @JvmStatic + fun enqueue() { + ApplicationDependencies.getJobManager().add( + GroupRingCleanupJob( + Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setLifespan(TimeUnit.HOURS.toMillis(1)) + .setMaxInstancesForFactory(1) + .setQueue(KEY) + .build() + ) + ) + } + } + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + override fun onRun() { + SignalDatabase.calls.getLatestRingingCalls().forEach { + ApplicationDependencies.getSignalCallManager().peekGroupCall(it.peer) + } + + SignalDatabase.calls.markRingingCallsAsMissed() + } + + override fun onShouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): GroupRingCleanupJob { + return GroupRingCleanupJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 15dab0058d..d4c06f116b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -129,6 +129,7 @@ public final class JobManagerFactories { put(GroupCallUpdateSendJob.KEY, new GroupCallUpdateSendJob.Factory()); put(GroupCallPeekJob.KEY, new GroupCallPeekJob.Factory()); put(GroupCallPeekWorkerJob.KEY, new GroupCallPeekWorkerJob.Factory()); + put(GroupRingCleanupJob.KEY, new GroupRingCleanupJob.Factory()); put(GroupV2UpdateSelfProfileKeyJob.KEY, new GroupV2UpdateSelfProfileKeyJob.Factory()); put(IndividualSendJob.KEY, new IndividualSendJob.Factory()); put(LeaveGroupV2Job.KEY, new LeaveGroupV2Job.Factory());