From e8c9122fdf6ad71066ce827d41c5a3502a7b95c0 Mon Sep 17 00:00:00 2001 From: TCFDONMEZ Date: Wed, 30 Sep 2026 11:02:08 +0300 Subject: [PATCH] fix: use DownloadManager for OTA packages --- CHANGELOG.md | 7 ++ FatihsMG4-unstable-2.2.17.apk.sha256 | 1 + app/build.gradle | 4 +- .../java/com/evsuite/abrp/OtaUpdater.java | 104 +++++++++--------- 4 files changed, 61 insertions(+), 55 deletions(-) create mode 100644 FatihsMG4-unstable-2.2.17.apk.sha256 diff --git a/CHANGELOG.md b/CHANGELOG.md index ad3fbf2..82bb49a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.2.17] - 2026-09-30 + +### Fixed + +- Replaced the custom OTA downloader with DriveHub_Dort's Android DownloadManager flow, + then stages and verifies the downloaded bytes before installation. + ## [2.2.16] - 2026-09-30 ### Added diff --git a/FatihsMG4-unstable-2.2.17.apk.sha256 b/FatihsMG4-unstable-2.2.17.apk.sha256 new file mode 100644 index 0000000..e9fb93b --- /dev/null +++ b/FatihsMG4-unstable-2.2.17.apk.sha256 @@ -0,0 +1 @@ +fa9649f4559b590f4514556805d95e2accc9f95298e8933c2a4f7e7af1ad075d FatihsMG4-unstable-2.2.17.apk diff --git a/app/build.gradle b/app/build.gradle index 70e59c8..c15b08b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,8 +34,8 @@ android { applicationId "com.evsuite.abrp" minSdk 27 targetSdk 36 - versionCode 33 - versionName "2.2.16" + versionCode 34 + versionName "2.2.17" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java b/app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java index 57f63f7..35f47e5 100644 --- a/app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java +++ b/app/src/unstable/java/com/evsuite/abrp/OtaUpdater.java @@ -1,14 +1,17 @@ package com.evsuite.abrp; import android.app.PendingIntent; +import android.app.DownloadManager; import android.content.ClipData; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller; import android.content.pm.ResolveInfo; +import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; +import android.os.ParcelFileDescriptor; import android.provider.Settings; import android.util.Log; @@ -346,64 +349,59 @@ final class OtaUpdater { Log.w(TAG, "Refusing to download from " + update.apkUrl); return null; } - File target = new File(context.getCacheDir(), CACHE_PREFIX - + java.util.UUID.randomUUID() + ".apk"); - File temporary = null; + DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); + if (manager == null) return null; + String publicName = downloadFileName(update.versionName); + File publicFile = new File(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOWNLOADS), publicName); + if (publicFile.exists() && !publicFile.delete()) return null; + long downloadId = -1L; try { - temporary = File.createTempFile(CACHE_PREFIX, ".apk", context.getCacheDir()); - URL current = new URL(update.apkUrl); - for (int redirects = 0; redirects <= 5; redirects++) { - if (!isAllowedUrl(current.toString())) return null; - HttpURLConnection connection = (HttpURLConnection) current.openConnection(); - connection.setInstanceFollowRedirects(false); - connection.setConnectTimeout(TIMEOUT_MS); - connection.setReadTimeout(TIMEOUT_MS); - try { - int status = connection.getResponseCode(); - if (status >= 300 && status <= 399) { - String location = connection.getHeaderField("Location"); - if (location == null) return null; - current = current.toURI().resolve(location).toURL(); - if (!isAllowedUrl(current.toString())) return null; - continue; - } - if (status != HttpURLConnection.HTTP_OK) return null; - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - long total = connection.getContentLengthLong(); - long received = 0; - int lastPercent = -1; - try (FileOutputStream output = new FileOutputStream(temporary)) { - try (java.io.InputStream input = connection.getInputStream()) { - byte[] buffer = new byte[32 * 1024]; - int count; - while ((count = input.read(buffer)) != -1) { - output.write(buffer, 0, count); - digest.update(buffer, 0, count); - received += count; - int percent = total > 0 ? (int) Math.min(100, received * 100 / total) : -1; - if (listener != null && percent != lastPercent) { - listener.onProgress(percent); - lastPercent = percent; - } - } - } - output.getFD().sync(); - } - String actual = hex(digest.digest()); - if (!actual.equalsIgnoreCase(expectedSha256)) { - Log.w(TAG, "Downloaded APK SHA-256 mismatch"); - return null; - } - if (!temporary.renameTo(target)) return null; - return target; - } finally { - connection.disconnect(); + DownloadManager.Request request = new DownloadManager.Request(Uri.parse(update.apkUrl)) + .setTitle("EVABRPUploader " + update.versionName) + .setDescription("Güncelleme indiriliyor") + .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) + .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, publicName) + .setAllowedOverMetered(true) + .setAllowedOverRoaming(true); + downloadId = manager.enqueue(request); + while (true) { + try (Cursor cursor = manager.query(new DownloadManager.Query().setFilterById(downloadId))) { + if (cursor == null || !cursor.moveToFirst()) return null; + int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); + long received = cursor.getLong(cursor.getColumnIndexOrThrow( + DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); + long total = cursor.getLong(cursor.getColumnIndexOrThrow( + DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); + if (listener != null) listener.onProgress(total > 0 + ? (int) Math.min(100, received * 100 / total) : -1); + if (status == DownloadManager.STATUS_SUCCESSFUL) break; + if (status == DownloadManager.STATUS_FAILED) return null; } + Thread.sleep(250L); } + + File staged = new File(context.getCacheDir(), CACHE_PREFIX + + java.util.UUID.randomUUID() + ".apk"); + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + try (ParcelFileDescriptor descriptor = manager.openDownloadedFile(downloadId); + java.io.InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(descriptor); + FileOutputStream output = new FileOutputStream(staged)) { + byte[] buffer = new byte[64 * 1024]; + int count; + while ((count = input.read(buffer)) != -1) { + output.write(buffer, 0, count); + digest.update(buffer, 0, count); + } + output.getFD().sync(); + } + if (!hex(digest.digest()).equalsIgnoreCase(expectedSha256)) { + staged.delete(); + return null; + } + return staged; } catch (Exception e) { Log.w(TAG, "Update download failed", e); - } finally { - if (temporary != null && temporary.exists()) temporary.delete(); } return null; }