From db9a7723a805d91a738cdd1723f1ec70c0413784 Mon Sep 17 00:00:00 2001 From: Rahul Patel Date: Sat, 30 May 2026 05:58:15 +0530 Subject: [PATCH] Fall back to session installer when chosen installer is unavailable Address #1386 The Shizuku branch of getPreferredInstaller() already fell back to the session installer, but the guard hasShizukuPerm() called Shizuku.checkSelfPermission(), which throws when the binder is not alive (Shizuku disabled). The exception escaped before the fallback could run, so installs failed instead of falling back. - Guard hasShizukuPerm() on Shizuku.pingBinder() and swallow failures so it returns false (instead of throwing) when Shizuku is unavailable, letting the existing fallback run. This also fixes canInstallSilently(). - Detect when any chosen installer resolves to the default and optionally inform the user via a toast, gated by notifyOnFallback so cancel/remove callers stay silent while real install paths surface it. --- .../store/data/helper/DownloadHelper.kt | 5 +- .../store/data/installer/AppInstaller.kt | 69 +++++++++++++------ .../aurora/store/data/work/DownloadWorker.kt | 2 +- .../viewmodel/downloads/DownloadsViewModel.kt | 2 +- app/src/main/res/values/strings.xml | 1 + 5 files changed, 54 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/aurora/store/data/helper/DownloadHelper.kt b/app/src/main/java/com/aurora/store/data/helper/DownloadHelper.kt index b948a4e25..640aaf120 100644 --- a/app/src/main/java/com/aurora/store/data/helper/DownloadHelper.kt +++ b/app/src/main/java/com/aurora/store/data/helper/DownloadHelper.kt @@ -174,8 +174,9 @@ class DownloadHelper @Inject constructor( if (existing != null && existing.versionCode == download.versionCode) { if (existing.canInstall(context)) { Log.i(TAG, "${download.packageName} already downloaded, installing directly") - runCatching { appInstaller.getPreferredInstaller().install(existing) } - .onFailure { Log.e(TAG, "Failed to install ${download.packageName}", it) } + runCatching { + appInstaller.getPreferredInstaller(notifyOnFallback = true).install(existing) + }.onFailure { Log.e(TAG, "Failed to install ${download.packageName}", it) } return } if (existing.isActive) { diff --git a/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt index 4917a6433..c5f73c51d 100644 --- a/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt +++ b/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt @@ -25,13 +25,17 @@ import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build +import android.util.Log import androidx.core.content.pm.PackageInfoCompat import com.aurora.Constants.PACKAGE_NAME_GMS +import com.aurora.extensions.TAG import com.aurora.extensions.getUpdateOwnerPackageNameCompat import com.aurora.extensions.isOAndAbove import com.aurora.extensions.isPAndAbove import com.aurora.extensions.isSAndAbove +import com.aurora.extensions.toast import com.aurora.store.BuildConfig +import com.aurora.store.R import com.aurora.store.data.installer.ShizukuInstaller.Companion.SHIZUKU_PACKAGE_NAME import com.aurora.store.data.installer.base.IInstaller import com.aurora.store.data.model.Installer @@ -172,8 +176,15 @@ class AppInstaller @Inject constructor( fun hasShizukuOrSui(context: Context): Boolean = isOAndAbove && (PackageUtil.isInstalled(context, SHIZUKU_PACKAGE_NAME) || Sui.isSui()) - fun hasShizukuPerm(): Boolean = - Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED + // Shizuku.checkSelfPermission() throws when the binder is not alive (Shizuku + // disabled/not running), so guard on pingBinder() and swallow any failure to let + // callers fall back gracefully instead of crashing. + fun hasShizukuPerm(): Boolean = try { + Shizuku.pingBinder() && + Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED + } catch (_: Exception) { + false + } fun uninstall(context: Context, packageName: String) { val intent = Intent().apply { @@ -196,31 +207,47 @@ class AppInstaller @Inject constructor( fun getMicroGInstaller(): IInstaller = microGInstaller - fun getPreferredInstaller(): IInstaller = when (getCurrentInstaller(context)) { - Installer.SESSION -> sessionInstaller + /** + * Returns the installer for the user's chosen mode, transparently falling back to the + * default (session) installer when that mode is currently unavailable, e.g. the chosen + * Shizuku installer when Shizuku is disabled. + * @param notifyOnFallback Whether to inform the user via a toast when a fallback happens + */ + fun getPreferredInstaller(notifyOnFallback: Boolean = false): IInstaller { + val selected = getCurrentInstaller(context) + val installer = when (selected) { + Installer.SESSION -> sessionInstaller - Installer.NATIVE -> nativeInstaller + Installer.NATIVE -> nativeInstaller - Installer.ROOT -> if (hasRootAccess()) rootInstaller else defaultInstaller + Installer.ROOT -> if (hasRootAccess()) rootInstaller else defaultInstaller - Installer.SERVICE -> if (hasAuroraService(context)) { - serviceInstaller - } else { - defaultInstaller + Installer.SERVICE -> if (hasAuroraService(context)) { + serviceInstaller + } else { + defaultInstaller + } + + Installer.AM -> if (hasAppManager(context)) amInstaller else defaultInstaller + + Installer.SHIZUKU -> if (hasShizukuOrSui(context) && hasShizukuPerm()) { + shizukuInstaller + } else { + defaultInstaller + } + + Installer.MICROG -> if (hasMicroGInstaller(context)) { + microGInstaller + } else { + defaultInstaller + } } - Installer.AM -> if (hasAppManager(context)) amInstaller else defaultInstaller - - Installer.SHIZUKU -> if (hasShizukuOrSui(context) && hasShizukuPerm()) { - shizukuInstaller - } else { - defaultInstaller + if (selected != Installer.SESSION && installer === defaultInstaller) { + Log.i(TAG, "$selected installer unavailable, falling back to session installer") + if (notifyOnFallback) context.toast(R.string.installer_fallback_session) } - Installer.MICROG -> if (hasMicroGInstaller(context)) { - microGInstaller - } else { - defaultInstaller - } + return installer } } diff --git a/app/src/main/java/com/aurora/store/data/work/DownloadWorker.kt b/app/src/main/java/com/aurora/store/data/work/DownloadWorker.kt index 9a972665c..260ae9de3 100644 --- a/app/src/main/java/com/aurora/store/data/work/DownloadWorker.kt +++ b/app/src/main/java/com/aurora/store/data/work/DownloadWorker.kt @@ -252,7 +252,7 @@ class DownloadWorker @AssistedInject constructor( private suspend fun onSuccess(): Result { return withContext(NonCancellable) { return@withContext try { - appInstaller.getPreferredInstaller().install(download) + appInstaller.getPreferredInstaller(notifyOnFallback = true).install(download) Result.success() } catch (exception: Exception) { Log.e(TAG, "Failed to install ${download.packageName}", exception) diff --git a/app/src/main/java/com/aurora/store/viewmodel/downloads/DownloadsViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/downloads/DownloadsViewModel.kt index a5e8ca1ed..ff3855ead 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/downloads/DownloadsViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/downloads/DownloadsViewModel.kt @@ -75,7 +75,7 @@ class DownloadsViewModel @Inject constructor( fun install(download: Download) { try { - appInstaller.getPreferredInstaller().install(download) + appInstaller.getPreferredInstaller(notifyOnFallback = true).install(download) } catch (exception: Exception) { Log.e(TAG, "Failed to install ${download.packageName}", exception) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7084817f4..8a0fc49e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -178,6 +178,7 @@ Install App Manager or change the installer. No root access. Grant it or change the installer. Shizuku is not installed or set up properly. + Chosen installer is unavailable, using the default installer instead. "Aurora Services is available and ready to install." Install Aurora Services 1.0.9 or above, or change the installer. Set up Aurora Services and grant all permissions first.