From 7f6e96a5228c03d4e4a9c28fa618a8bc2e70e4fb Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Mon, 13 Apr 2026 14:27:18 +0000 Subject: [PATCH] Check DownloadManager status to properly detect errors. --- .../securesms/apkupdate/ApkUpdateInstaller.kt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/apkupdate/ApkUpdateInstaller.kt b/app/src/main/java/org/thoughtcrime/securesms/apkupdate/ApkUpdateInstaller.kt index b4b5c0dc05..ef0ff39a1a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/apkupdate/ApkUpdateInstaller.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/apkupdate/ApkUpdateInstaller.kt @@ -5,6 +5,7 @@ package org.thoughtcrime.securesms.apkupdate +import android.app.DownloadManager import android.app.PendingIntent import android.content.Context import android.content.Intent @@ -53,6 +54,13 @@ object ApkUpdateInstaller { return } + if (!isDownloadSuccessful(context, downloadId)) { + Log.w(TAG, "DownloadId matches, but the download was not successful. The download may have failed due to a network issue. Clearing state and re-checking for updates.") + SignalStore.apkUpdate.clearDownloadAttributes() + AppDependencies.jobManager.add(ApkUpdateJob()) + return + } + if (!isMatchingDigest(context, downloadId, digest)) { Log.w(TAG, "DownloadId matches, but digest does not! Bad download or inconsistent state. Failing and clearing state.") SignalStore.apkUpdate.clearDownloadAttributes() @@ -136,6 +144,35 @@ object ApkUpdateInstaller { } } + private fun isDownloadSuccessful(context: Context, downloadId: Long): Boolean { + val query = DownloadManager.Query().setFilterById(downloadId) + val cursor = context.getDownloadManager().query(query) + + return cursor.use { cursor -> + if (cursor.moveToFirst()) { + val status = cursor + .getColumnIndex(DownloadManager.COLUMN_STATUS) + .takeUnless { it == -1 } + ?.let { cursor.getInt(it) } ?: DownloadManager.STATUS_FAILED + + if (status == DownloadManager.STATUS_SUCCESSFUL) { + return@use true + } + + val reason = cursor + .getColumnIndex(DownloadManager.COLUMN_REASON) + .takeUnless { it == -1 } + ?.let { cursor.getInt(it) } + + Log.w(TAG, "Download not successful. Status: $status, Reason: $reason") + false + } else { + Log.w(TAG, "Download ID $downloadId not found in DownloadManager.") + false + } + } + } + private fun isMatchingDigest(context: Context, downloadId: Long, expectedDigest: ByteArray): Boolean { return try { FileInputStream(context.getDownloadManager().openDownloadedFile(downloadId).fileDescriptor).use { stream ->