fix: observe cancellation before promoting a verified APK

Codex is right about this one. Cancellation in Kotlin is cooperative, and
everything from validateDownloadedApk() through saveMetadata() is plain
blocking code with no suspension point. Stopping during the signature
check was therefore not observed until after the temp file had been
renamed and its metadata written, so the worker committed the APK while
WorkManager reported the work cancelled.

That also raced onCancelDownload(): its checkStatus() could read the cache
before the commit and settle on NotDownloaded, after which the cancelled
work maps to Idle and the observer ignores it. The row then advertised
"Not ready" with a verified universal APK already in the cache, and
tapping it downloaded the same bytes again.

One checkpoint after validation, which is the slow step and so the most
likely moment to press Stop. The verified temp file is left in place, so
the next attempt resumes rather than starting over.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Moe Hamade 2026-08-01 16:01:08 +03:00
parent 408d1c760a
commit b60b5121aa

View File

@ -9,6 +9,7 @@ import com.bitchat.android.net.ArtiTorManager
import com.bitchat.android.net.OkHttpProvider
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import okhttp3.Call
@ -217,6 +218,12 @@ class UniversalApkManager(
phaseCallback?.invoke(ApkDownloader.DownloadPhase.VerifyingSignature)
validateDownloadedApk(tempFile, source)
// Everything from here to the metadata write is plain blocking code, so a
// cancellation arriving during the (slow) signature check would otherwise go
// unobserved and commit the APK anyway. The verified temp file survives for
// resume; only the promotion is abandoned.
ensureActive()
val version = downloadedVersionName(tempFile)
val safeVersion = version.replace(Regex("[^A-Za-z0-9._-]"), "_")
val finalFileName = "$APK_FILE_PREFIX$safeVersion.apk"