fix: open system installer when OTA session fails
This commit is contained in:
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
applicationId "com.evsuite.abrp"
|
||||
minSdk 27
|
||||
targetSdk 36
|
||||
versionCode 28
|
||||
versionName "2.2.11"
|
||||
versionCode 29
|
||||
versionName "2.2.12"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,16 @@
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<application>
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/ota_file_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver
|
||||
android:name=".OtaInstallResultReceiver"
|
||||
android:exported="false">
|
||||
|
||||
@@ -4,8 +4,13 @@ import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -414,6 +419,8 @@ final class OtaUpdater {
|
||||
UNREADABLE_ARCHIVE,
|
||||
/** Handed to the platform. The outcome arrives at {@link OtaInstallResultReceiver}. */
|
||||
SESSION_STARTED,
|
||||
/** Session install was unavailable; the system's interactive installer was opened. */
|
||||
INSTALL_UI_STARTED,
|
||||
/** The session could not be created, written or committed. */
|
||||
SESSION_FAILED
|
||||
}
|
||||
@@ -437,8 +444,14 @@ final class OtaUpdater {
|
||||
commitSession(context, apk);
|
||||
return InstallResult.SESSION_STARTED;
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "OTA session install failed", t);
|
||||
return InstallResult.SESSION_FAILED;
|
||||
Log.w(TAG, "OTA session install failed; trying system install UI", t);
|
||||
try {
|
||||
launchInstallerActivity(context, apk);
|
||||
return InstallResult.INSTALL_UI_STARTED;
|
||||
} catch (Throwable fallback) {
|
||||
Log.w(TAG, "OTA install UI failed", fallback);
|
||||
return InstallResult.SESSION_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,4 +524,41 @@ final class OtaUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
/** DriveHub_Dort-compatible fallback for head units that refuse installer sessions. */
|
||||
private static void launchInstallerActivity(Context context, File apk) {
|
||||
if (Build.VERSION.SDK_INT >= 26
|
||||
&& !context.getPackageManager().canRequestPackageInstalls()) {
|
||||
Intent settings = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
|
||||
.setData(Uri.parse("package:" + context.getPackageName()))
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(settings);
|
||||
throw new IllegalStateException("unknown sources permission required");
|
||||
}
|
||||
|
||||
Uri contentUri = FileProvider.getUriForFile(context,
|
||||
context.getPackageName() + ".fileprovider", apk);
|
||||
Intent install = new Intent(Intent.ACTION_VIEW)
|
||||
.setDataAndType(contentUri, "application/vnd.android.package-archive")
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
grantToInstallers(context, contentUri);
|
||||
if (install.resolveActivity(context.getPackageManager()) == null) {
|
||||
throw new IllegalStateException("No package installer activity");
|
||||
}
|
||||
context.startActivity(install);
|
||||
Log.i(TAG, "System install UI opened for " + apk.getName());
|
||||
}
|
||||
|
||||
private static void grantToInstallers(Context context, Uri uri) {
|
||||
String[] packages = {
|
||||
"com.android.packageinstaller",
|
||||
"com.google.android.packageinstaller",
|
||||
"com.samsung.android.packageinstaller"
|
||||
};
|
||||
for (String packageName : packages) {
|
||||
try {
|
||||
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
} catch (Throwable ignored) { }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -117,10 +117,12 @@ final class UpdateHook {
|
||||
if (apk == null) return "İndirme veya SHA-256 doğrulaması başarısız ("
|
||||
+ update.versionName + ")";
|
||||
|
||||
// The cached APK is cleared whichever way install goes: a rejected archive must not
|
||||
// linger, and an accepted one has already been streamed into the installer session.
|
||||
OtaUpdater.InstallResult result = OtaUpdater.install(app, apk);
|
||||
if (!apk.delete()) Log.w(TAG, "Could not remove cached OTA APK");
|
||||
// FileProvider's interactive installer opens asynchronously and still needs this file.
|
||||
// Other outcomes have either streamed the bytes already or rejected the archive.
|
||||
if (result != OtaUpdater.InstallResult.INSTALL_UI_STARTED && !apk.delete()) {
|
||||
Log.w(TAG, "Could not remove cached OTA APK");
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case SESSION_STARTED:
|
||||
@@ -128,6 +130,8 @@ final class UpdateHook {
|
||||
// platform may still want the driver to confirm, and either way the verdict
|
||||
// arrives at OtaInstallResultReceiver, not here.
|
||||
return "Kuruluyor: " + update.versionName + " — ekrandaki onayı bekleyin";
|
||||
case INSTALL_UI_STARTED:
|
||||
return "Kurulum ekranı açıldı: " + update.versionName;
|
||||
case UNREADABLE_ARCHIVE:
|
||||
return "Kurulum reddedildi: APK okunamadı";
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path name="ota_cache" path="." />
|
||||
</paths>
|
||||
Reference in New Issue
Block a user