Skip to content

Commit ea29675

Browse files
committed
fix: OTA sha verification works
1 parent f961e89 commit ea29675

3 files changed

Lines changed: 69 additions & 21 deletions

File tree

‎app/src/main/java/com/drivehub/kamera/OtaApkVerifier.java‎

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.content.Context;
44
import android.net.Uri;
5+
import android.os.ParcelFileDescriptor;
56

67
import java.io.File;
78
import java.io.FileInputStream;
@@ -11,15 +12,23 @@
1112

1213
final class OtaApkVerifier {
1314

15+
private static final int OPEN_RETRY_COUNT = 5;
16+
private static final long OPEN_RETRY_DELAY_MS = 300L;
17+
1418
private OtaApkVerifier() {
1519
}
1620

1721
// Verification is intentionally isolated so download integrity stays testable without UI code.
18-
static VerificationResult verifyDownloadedApk(Context context, Uri apkUri, OtaUpdateManager.UpdateInfo info) {
22+
static VerificationResult verifyDownloadedApk(
23+
Context context,
24+
long downloadId,
25+
Uri apkUri,
26+
OtaUpdateManager.UpdateInfo info
27+
) {
1928
boolean success;
2029
String computed = "";
2130
String message;
22-
try (InputStream in = openApkStream(context, apkUri)) {
31+
try (InputStream in = openApkStream(context, downloadId, apkUri)) {
2332
if (info == null || info.expectedSha256 == null || info.expectedSha256.isEmpty()) {
2433
throw new IllegalStateException("Missing expected SHA-256");
2534
}
@@ -28,32 +37,70 @@ static VerificationResult verifyDownloadedApk(Context context, Uri apkUri, OtaUp
2837
message = success ? "OK" : "SHA256 mismatch";
2938
} catch (Exception e) {
3039
success = false;
31-
message = e.getClass().getSimpleName();
40+
String detail = e.getMessage();
41+
message = (detail == null || detail.trim().isEmpty())
42+
? e.getClass().getSimpleName()
43+
: e.getClass().getSimpleName() + ": " + detail;
3244
}
3345
return new VerificationResult(success, computed, message);
3446
}
3547

36-
private static InputStream openApkStream(Context context, Uri apkUri) throws Exception {
48+
private static InputStream openApkStream(Context context, long downloadId, Uri apkUri) throws Exception {
3749
if (context == null) {
3850
throw new IllegalStateException("Context missing");
3951
}
40-
if (apkUri == null) {
41-
throw new IllegalStateException("Downloaded APK not found");
42-
}
43-
String scheme = apkUri.getScheme();
44-
if ("file".equalsIgnoreCase(scheme)) {
45-
File apkFile = new File(apkUri.getPath());
46-
if (!apkFile.exists()) {
47-
throw new IllegalStateException("Downloaded APK not found");
52+
Exception lastError = null;
53+
for (int attempt = 0; attempt < OPEN_RETRY_COUNT; attempt += 1) {
54+
try {
55+
InputStream fromDownloadManager = openDownloadedFileStream(context, downloadId);
56+
if (fromDownloadManager != null) {
57+
return fromDownloadManager;
58+
}
59+
if (apkUri == null) {
60+
throw new IllegalStateException("Downloaded APK not found");
61+
}
62+
String scheme = apkUri.getScheme();
63+
if ("file".equalsIgnoreCase(scheme)) {
64+
File apkFile = new File(apkUri.getPath());
65+
if (!apkFile.exists()) {
66+
throw new IllegalStateException("Downloaded APK not found");
67+
}
68+
return new FileInputStream(apkFile);
69+
}
70+
71+
InputStream stream = context.getContentResolver().openInputStream(apkUri);
72+
if (stream != null) {
73+
return stream;
74+
}
75+
throw new IllegalStateException("Downloaded APK stream unavailable");
76+
} catch (Exception e) {
77+
lastError = e;
78+
if (attempt == OPEN_RETRY_COUNT - 1) {
79+
break;
80+
}
81+
try {
82+
Thread.sleep(OPEN_RETRY_DELAY_MS);
83+
} catch (InterruptedException interrupted) {
84+
Thread.currentThread().interrupt();
85+
throw new IllegalStateException("Verification interrupted", interrupted);
86+
}
4887
}
49-
return new FileInputStream(apkFile);
5088
}
89+
throw lastError != null ? lastError : new IllegalStateException("Downloaded APK not found");
90+
}
5191

52-
InputStream stream = context.getContentResolver().openInputStream(apkUri);
53-
if (stream == null) {
54-
throw new IllegalStateException("Downloaded APK not found");
92+
private static InputStream openDownloadedFileStream(Context context, long downloadId) {
93+
if (downloadId <= 0L) return null;
94+
try {
95+
android.app.DownloadManager dm =
96+
(android.app.DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
97+
if (dm == null) return null;
98+
ParcelFileDescriptor pfd = dm.openDownloadedFile(downloadId);
99+
if (pfd == null) return null;
100+
return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
101+
} catch (Throwable ignored) {
102+
return null;
55103
}
56-
return stream;
57104
}
58105

59106
private static String computeSha256(File file) throws Exception {

‎app/src/main/java/com/drivehub/kamera/OtaController.java‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ private void maybeStartDownload(OtaUpdateManager.UpdateInfo info) {
130130

131131
private void startDownload(OtaUpdateManager.UpdateInfo info) {
132132
try {
133+
// Clear any stale watcher state before we bind the new release metadata to the dialog.
134+
stopProgressWatcher();
133135
long downloadId = OtaUpdateManager.enqueueDownload(activity, info);
134136
activeDownloadInfo = info;
135137
showProgressDialog(info, downloadId);
@@ -142,7 +144,6 @@ private void startDownload(OtaUpdateManager.UpdateInfo info) {
142144
}
143145

144146
private void showProgressDialog(OtaUpdateManager.UpdateInfo info, long downloadId) {
145-
stopProgressWatcher();
146147
OtaDialogs.ProgressDialogHandle handle = OtaDialogs.showProgressDialog(
147148
activity,
148149
activity.getString(R.string.ota_dialog_download_started_message, info.latestVersion),
@@ -274,7 +275,7 @@ private void startVerification(long downloadId, TextView statusView, View instal
274275
return;
275276
}
276277

277-
OtaUpdateManager.verifyDownloadedApk(activity, apkUri, activeDownloadInfo, (success, computedSha256, message) -> {
278+
OtaUpdateManager.verifyDownloadedApk(activity, downloadId, apkUri, activeDownloadInfo, (success, computedSha256, message) -> {
278279
if (downloadId != verificationDownloadId) return;
279280
verificationInFlight = false;
280281
verificationPassed = success;

‎app/src/main/java/com/drivehub/kamera/OtaUpdateManager.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,11 @@ static long enqueueDownload(Context context, UpdateInfo info) {
148148
return dm.enqueue(request);
149149
}
150150

151-
static void verifyDownloadedApk(Context context, Uri apkUri, UpdateInfo info, VerifyCallback callback) {
151+
static void verifyDownloadedApk(Context context, long downloadId, Uri apkUri, UpdateInfo info, VerifyCallback callback) {
152152
final Context appContext = context.getApplicationContext();
153153
EXECUTOR.execute(() -> {
154154
OtaApkVerifier.VerificationResult result =
155-
OtaApkVerifier.verifyDownloadedApk(appContext, apkUri, info);
155+
OtaApkVerifier.verifyDownloadedApk(appContext, downloadId, apkUri, info);
156156
MAIN_HANDLER.post(() -> callback.onResult(result.success, result.computedSha256, result.message));
157157
});
158158
}

0 commit comments

Comments
 (0)