fix: stage OTA fallback in public downloads
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.14] - 2026-09-30
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Stage fallback OTA packages in public Downloads and grant the resolved system installer
|
||||||
|
explicit read access, avoiding the MG head unit's private-cache package parse failure.
|
||||||
|
|
||||||
## [2.2.13] - 2026-09-30
|
## [2.2.13] - 2026-09-30
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
2878b084beae8af2bf4295b18b996ac71d0ebcd3b8c11beb5453a3e7eb68f7d2 FatihsMG4-unstable-2.2.14.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 30
|
versionCode 31
|
||||||
versionName "2.2.13"
|
versionName "2.2.14"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
platform-signed build the platform may install without asking; when it does ask,
|
platform-signed build the platform may install without asking; when it does ask,
|
||||||
this is what lets the confirmation be raised at all. -->
|
this is what lets the confirmation be raised at all. -->
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="28" />
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
<provider
|
<provider
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package com.evsuite.abrp;
|
package com.evsuite.abrp;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
|
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.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -17,6 +20,7 @@ import org.json.JSONObject;
|
|||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -535,17 +539,41 @@ final class OtaUpdater {
|
|||||||
throw new IllegalStateException("unknown sources permission required");
|
throw new IllegalStateException("unknown sources permission required");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File publicApk = copyToPublicDownloads(apk);
|
||||||
Uri contentUri = FileProvider.getUriForFile(context,
|
Uri contentUri = FileProvider.getUriForFile(context,
|
||||||
context.getPackageName() + ".fileprovider", apk);
|
context.getPackageName() + ".fileprovider", publicApk);
|
||||||
Intent install = new Intent(Intent.ACTION_VIEW)
|
Intent install = new Intent(Intent.ACTION_VIEW)
|
||||||
.setDataAndType(contentUri, "application/vnd.android.package-archive")
|
.setDataAndType(contentUri, "application/vnd.android.package-archive")
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
install.setClipData(ClipData.newRawUri("EVABRPUploader update", contentUri));
|
||||||
grantToInstallers(context, contentUri);
|
grantToInstallers(context, contentUri);
|
||||||
if (install.resolveActivity(context.getPackageManager()) == null) {
|
if (install.resolveActivity(context.getPackageManager()) == null) {
|
||||||
throw new IllegalStateException("No package installer activity");
|
throw new IllegalStateException("No package installer activity");
|
||||||
}
|
}
|
||||||
context.startActivity(install);
|
context.startActivity(install);
|
||||||
Log.i(TAG, "System install UI opened for " + apk.getName());
|
Log.i(TAG, "System install UI opened for " + publicApk.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File copyToPublicDownloads(File source) {
|
||||||
|
File downloads = Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS);
|
||||||
|
if (downloads == null || (!downloads.isDirectory() && !downloads.mkdirs())) {
|
||||||
|
throw new IllegalStateException("Downloads directory unavailable");
|
||||||
|
}
|
||||||
|
File destination = new File(downloads, "FatihsMG4-OTA-update.apk");
|
||||||
|
try (FileInputStream input = new FileInputStream(source);
|
||||||
|
FileOutputStream output = new FileOutputStream(destination, false)) {
|
||||||
|
byte[] buffer = new byte[64 * 1024];
|
||||||
|
int count;
|
||||||
|
while ((count = input.read(buffer)) != -1) output.write(buffer, 0, count);
|
||||||
|
output.getFD().sync();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Could not stage APK in Downloads", e);
|
||||||
|
}
|
||||||
|
if (destination.length() != source.length()) {
|
||||||
|
throw new IllegalStateException("Staged APK size mismatch");
|
||||||
|
}
|
||||||
|
return destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void grantToInstallers(Context context, Uri uri) {
|
private static void grantToInstallers(Context context, Uri uri) {
|
||||||
@@ -559,6 +587,15 @@ final class OtaUpdater {
|
|||||||
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
} catch (Throwable ignored) { }
|
} catch (Throwable ignored) { }
|
||||||
}
|
}
|
||||||
|
Intent probe = new Intent(Intent.ACTION_VIEW)
|
||||||
|
.setDataAndType(uri, "application/vnd.android.package-archive");
|
||||||
|
for (ResolveInfo info : context.getPackageManager().queryIntentActivities(probe, 0)) {
|
||||||
|
if (info.activityInfo == null) continue;
|
||||||
|
try {
|
||||||
|
context.grantUriPermission(info.activityInfo.packageName, uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
} catch (Throwable ignored) { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths>
|
<paths>
|
||||||
<cache-path name="ota_cache" path="." />
|
<cache-path name="ota_cache" path="." />
|
||||||
|
<external-path name="downloads" path="Download/" />
|
||||||
</paths>
|
</paths>
|
||||||
|
|||||||
Reference in New Issue
Block a user