fix: use DownloadManager for OTA packages
This commit is contained in:
@@ -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
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
||||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
[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
|
## [2.2.16] - 2026-09-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
fa9649f4559b590f4514556805d95e2accc9f95298e8933c2a4f7e7af1ad075d FatihsMG4-unstable-2.2.17.apk
|
||||||
+2
-2
@@ -34,8 +34,8 @@ android {
|
|||||||
applicationId "com.evsuite.abrp"
|
applicationId "com.evsuite.abrp"
|
||||||
minSdk 27
|
minSdk 27
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 33
|
versionCode 34
|
||||||
versionName "2.2.16"
|
versionName "2.2.17"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package com.evsuite.abrp;
|
package com.evsuite.abrp;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
import android.app.DownloadManager;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageInstaller;
|
import android.content.pm.PackageInstaller;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -346,64 +349,59 @@ final class OtaUpdater {
|
|||||||
Log.w(TAG, "Refusing to download from " + update.apkUrl);
|
Log.w(TAG, "Refusing to download from " + update.apkUrl);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
File target = new File(context.getCacheDir(), CACHE_PREFIX
|
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||||
+ java.util.UUID.randomUUID() + ".apk");
|
if (manager == null) return null;
|
||||||
File temporary = 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 {
|
try {
|
||||||
temporary = File.createTempFile(CACHE_PREFIX, ".apk", context.getCacheDir());
|
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(update.apkUrl))
|
||||||
URL current = new URL(update.apkUrl);
|
.setTitle("EVABRPUploader " + update.versionName)
|
||||||
for (int redirects = 0; redirects <= 5; redirects++) {
|
.setDescription("Güncelleme indiriliyor")
|
||||||
if (!isAllowedUrl(current.toString())) return null;
|
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||||
HttpURLConnection connection = (HttpURLConnection) current.openConnection();
|
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, publicName)
|
||||||
connection.setInstanceFollowRedirects(false);
|
.setAllowedOverMetered(true)
|
||||||
connection.setConnectTimeout(TIMEOUT_MS);
|
.setAllowedOverRoaming(true);
|
||||||
connection.setReadTimeout(TIMEOUT_MS);
|
downloadId = manager.enqueue(request);
|
||||||
try {
|
while (true) {
|
||||||
int status = connection.getResponseCode();
|
try (Cursor cursor = manager.query(new DownloadManager.Query().setFilterById(downloadId))) {
|
||||||
if (status >= 300 && status <= 399) {
|
if (cursor == null || !cursor.moveToFirst()) return null;
|
||||||
String location = connection.getHeaderField("Location");
|
int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
|
||||||
if (location == null) return null;
|
long received = cursor.getLong(cursor.getColumnIndexOrThrow(
|
||||||
current = current.toURI().resolve(location).toURL();
|
DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
|
||||||
if (!isAllowedUrl(current.toString())) return null;
|
long total = cursor.getLong(cursor.getColumnIndexOrThrow(
|
||||||
continue;
|
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;
|
||||||
}
|
}
|
||||||
if (status != HttpURLConnection.HTTP_OK) return null;
|
Thread.sleep(250L);
|
||||||
|
}
|
||||||
|
|
||||||
|
File staged = new File(context.getCacheDir(), CACHE_PREFIX
|
||||||
|
+ java.util.UUID.randomUUID() + ".apk");
|
||||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
long total = connection.getContentLengthLong();
|
try (ParcelFileDescriptor descriptor = manager.openDownloadedFile(downloadId);
|
||||||
long received = 0;
|
java.io.InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(descriptor);
|
||||||
int lastPercent = -1;
|
FileOutputStream output = new FileOutputStream(staged)) {
|
||||||
try (FileOutputStream output = new FileOutputStream(temporary)) {
|
byte[] buffer = new byte[64 * 1024];
|
||||||
try (java.io.InputStream input = connection.getInputStream()) {
|
|
||||||
byte[] buffer = new byte[32 * 1024];
|
|
||||||
int count;
|
int count;
|
||||||
while ((count = input.read(buffer)) != -1) {
|
while ((count = input.read(buffer)) != -1) {
|
||||||
output.write(buffer, 0, count);
|
output.write(buffer, 0, count);
|
||||||
digest.update(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();
|
output.getFD().sync();
|
||||||
}
|
}
|
||||||
String actual = hex(digest.digest());
|
if (!hex(digest.digest()).equalsIgnoreCase(expectedSha256)) {
|
||||||
if (!actual.equalsIgnoreCase(expectedSha256)) {
|
staged.delete();
|
||||||
Log.w(TAG, "Downloaded APK SHA-256 mismatch");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!temporary.renameTo(target)) return null;
|
return staged;
|
||||||
return target;
|
|
||||||
} finally {
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, "Update download failed", e);
|
Log.w(TAG, "Update download failed", e);
|
||||||
} finally {
|
|
||||||
if (temporary != null && temporary.exists()) temporary.delete();
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user