From addfba217be6e4280159932028ff93a545162582 Mon Sep 17 00:00:00 2001 From: Muntashir Al-Islam Date: Sat, 11 Jan 2025 07:07:18 -0800 Subject: [PATCH] [Installer] Add workaround for HyperOS 2.0 When INSTALL_FAILED_HYPEROS_ISOLATION_VIOLATION is triggered in privileged mode, it tries again with the installer set to `com.android.shell`. Signed-off-by: Muntashir Al-Islam --- .../apk/installer/PackageInstallerCompat.java | 71 ++++++++++++++----- .../installer/PackageInstallerService.java | 10 +++ .../AppManager/backup/RestoreOp.java | 12 ++++ 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerCompat.java b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerCompat.java index c9ec3817de8..13c62649167 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerCompat.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerCompat.java @@ -476,8 +476,9 @@ public interface OnInstallListener { // MIUI-begin: MIUI 12.5+ workaround /** - * MIUI 12.5+ may require more than one tries in order to have successful installations. This is only needed - * during APK installations, not APK uninstallations or install-existing attempts. + * MIUI 12.5+ may require more than one tries in order to have successful installations. + * This is only needed during APK installations, not APK uninstallations or install-existing + * attempts. * * @param apkFile Underlying APK file if available. */ @@ -486,6 +487,20 @@ default void onAnotherAttemptInMiui(@Nullable ApkFile apkFile) { } // MIUI-end + // HyperOS-begin: HyperOS 2.0+ workaround + + /** + * In HyperOS 2.0+, the installer for the system apps must be another system app. The + * overridden method must set the package installer to a valid system app. This is only + * needed during APK installations, not APK uninstallations or install-existing attempts. + * + * @param apkFile Underlying APK file if available. + */ + @WorkerThread + default void onSecondAttemptInHyperOsWithoutInstaller(@Nullable ApkFile apkFile) { + } + // HyperOS-end + @WorkerThread void onFinishedInstall(int sessionId, String packageName, int result, @Nullable String blockingPackage, @Nullable String statusMessage); @@ -1033,25 +1048,45 @@ private void installCompleted(int sessionId, @Nullable String blockingPackage, @Nullable String statusMessage) { ThreadUtils.ensureWorkerThread(); - // MIUI-begin: In MIUI 12.5 and 20.2.0, it might be required to try installing the APK files more than once. if (finalStatus == STATUS_FAILURE_ABORTED && mSessionId == sessionId - && mOnInstallListener != null - && !SelfPermissions.checkSelfPermission(Manifest.permission.INSTALL_PACKAGES) - && MiuiUtils.isActualMiuiVersionAtLeast("12.5", "20.2.0") - && Objects.equals(statusMessage, "INSTALL_FAILED_ABORTED: Permission denied") - && mAttempts <= 3) { - // Try once more - ++mAttempts; - Log.i(TAG, "MIUI: Installation attempt no %d for package %s", mAttempts, mPackageName); - mInteractionWatcher.countDown(); - mInstallWatcher.countDown(); - // Remove old broadcast receivers - unregisterReceiver(); - mOnInstallListener.onAnotherAttemptInMiui(mApkFile); - return; + && mOnInstallListener != null) { + boolean privileged = SelfPermissions.checkSelfPermission(Manifest.permission.INSTALL_PACKAGES); + // MIUI-begin: In MIUI 12.5 and 20.2.0, it might be required to try installing the APK files more than once. + if (!privileged + && MiuiUtils.isActualMiuiVersionAtLeast("12.5", "20.2.0") + && Objects.equals(statusMessage, "INSTALL_FAILED_ABORTED: Permission denied") + && mAttempts <= 3) { + // Try once more + ++mAttempts; + Log.i(TAG, "MIUI: Installation attempt no %d for package %s", mAttempts, mPackageName); + mInteractionWatcher.countDown(); + mInstallWatcher.countDown(); + // Remove old broadcast receivers + unregisterReceiver(); + mOnInstallListener.onAnotherAttemptInMiui(mApkFile); + return; + } + // MIUI-end + // HyperOS-begin: In HyperOS 2.0, installer package needs to be altered + if (privileged + // TODO: 1/10/25 Check for HyperOS? + && statusMessage != null + && statusMessage.startsWith("INSTALL_FAILED_HYPEROS_ISOLATION_VIOLATION: ") + && mAttempts <= 2) { + // Try a second time with installer set to shell + ++mAttempts; + Log.i(TAG, "HyperOS: %s", statusMessage); + Log.i(TAG, "HyperOS: Second attempt for %s", mPackageName); + mInteractionWatcher.countDown(); + mInstallWatcher.countDown(); + // Remove old broadcast receivers + unregisterReceiver(); + mOnInstallListener.onSecondAttemptInHyperOsWithoutInstaller(mApkFile); + return; + } + // HyperOS-end } - // MIUI-end // No need to check package name since it's been checked before if (finalStatus == STATUS_FAILURE_SESSION_CREATE || (mSessionId == sessionId)) { if (mOnInstallListener != null) { diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerService.java b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerService.java index e44e8bba9f5..2417a7f9d8f 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerService.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/apk/installer/PackageInstallerService.java @@ -135,6 +135,16 @@ public void onAnotherAttemptInMiui(@Nullable ApkFile apkFile) { } // MIUI-end + // HyperOS-begin: HyperOS 2.0+ workaround + @Override + public void onSecondAttemptInHyperOsWithoutInstaller(@Nullable ApkFile apkFile) { + if (apkFile != null) { + options.setInstallerName("com.android.shell"); + installer.install(apkFile, selectedSplitIds, options, mProgressHandler); + } + } + // HyerOS-end + @Override public void onFinishedInstall(int sessionId, String packageName, int result, @Nullable String blockingPackage, @Nullable String statusMessage) { diff --git a/app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java b/app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java index dec72ca4cc1..59531fca703 100644 --- a/app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java +++ b/app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java @@ -356,12 +356,24 @@ private void restoreApkFiles() throws BackupException { public void onStartInstall(int sessionId, String packageName) { } + // MIUI-begin: MIUI 12.5+ workaround @Override public void onAnotherAttemptInMiui(@Nullable ApkFile apkFile) { // This works because the parent install method still remains active until a final status is // received after all the attempts are finished, which is, then, returned to the parent. packageInstaller.install(allApks, mPackageName, options); } + // MIUI-end + + // HyperOS-begin: HyperOS 2.0+ workaround + @Override + public void onSecondAttemptInHyperOsWithoutInstaller(@Nullable ApkFile apkFile) { + // This works because the parent install method still remains active until a final status is + // received after all the attempts are finished, which is, then, returned to the parent. + options.setInstallerName("com.android.shell"); + packageInstaller.install(allApks, mPackageName, options); + } + // HyperOS-end @Override public void onFinishedInstall(int sessionId, String packageName, int result, @Nullable String blockingPackage, @Nullable String statusMessage) {