Repository navigation
SplitInstall: Add sessions and SplitCompat delivery - #3903
DaVinci9196 wants to merge 5 commits into
Conversation
|
@DaVinci9196 Setup
Background: why Huawei is different So a native split install can never finish silently there. It ends in status 8 (requires user confirmation). For comparison, with the current release (0.3.17) one Facebook install from Aurora showed the Huawei install dialog 4 times in about an hour: once for the base APK and once each for maplibre, papaya and pytorch. Results with
Results with
This is a big improvement. Remaining issues on Huawei:
Suggestions
About #3543 The rest of #3543 is small: it sets If you're willing, I'd be glad if you could include that in this PR as well. You probably know a better way to implement it, and I'd like to learn from it. Then I'll close #3543. Logs (filtered, no account data) Current release 0.3.17 (Mate X6, Facebook, 4 dialogs):
|
Split installs are handled by microg#3903. Also correct the comment on USER_ACTION_NOT_REQUIRED: it does not skip the EMUI install dialog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@DaVinci9196
The base install through Aurora still shows the Huawei dialog, as expected. With 0.3.17, the dialog appears for every module (for example, 14 times for TikTok in 7 minutes). With this commit, feature modules install without any dialog. The only one left is for language splits, which I understand go through a session on purpose. Is there a reason they can't use SplitCompat too? One small thing in the Facebook log (19:47:54–19:47:58): Facebook made a foreground request for pytorch while deferred sessions were still active. It got About #3543: I'd like to close it, since this PR covers almost all of it. The only part left is setting Thanks again, this works very well on Huawei. Logs (filtered, no account data) TikTok: 3903-bfba381e-huawei-tiktok.log Netflix: 3903-bfba381e-huawei-netflix.log |
|
@grenadin We are currently evaluating whether SplitCompat mode can support language switching across different applications. However, our tests have revealed a couple of issues: Chrome fails to launch when a language pack is installed via SplitCompat, while in apps like Reddit, the language reverts to its original setting after the app is closed. If possible, could you try removing the conditional checks and test whether language switching works as expected in these applications? We would greatly appreciate your help in determining whether these issues can be resolved and, if so, identifying a potential fix. |
|
@DaVinci9196
To your question whether these issues can be fixed: for Chrome, I don't think it can be fixed in Companion. Chromium says so in For Reddit, the result wasn't consistent. It restarts itself within about 40 ms after the split is delivered, so it may be a timing issue between that restart and SplitCompat copying the split, but I couldn't confirm it. I haven't decompiled it, and I can't explain why Japanese never worked. Real Play installs language splits natively, so I think keeping them native, as About status 8 on Huawei Chrome doesn't handle First I tried to let Companion open the confirmation itself when the app doesn't within 3 seconds, like 0.3.17 does. Android blocked it as a background activity start, also when I opened it while still handling the installer result. As far as I can tell, 0.3.17 only gets away with it because the client is still bound to What worked instead was a separate high-importance channel for the install prompt, so it pops up as a heads-up notification. While that notification can be shown, I didn't report status 8 to the app, so every app gets the same prompt. If Companion's notifications or that channel are turned off, status 8 is reported as now, so apps like Reddit can still show the confirmation. Results on the MatePad:
This differs from Play, which reports status 8 to the app. I also didn't change what Diff against bfba381diff --git a/vending-app/src/main/java/org/microg/vending/ui/InstallProgressNotification.kt b/vending-app/src/main/java/org/microg/vending/ui/InstallProgressNotification.kt
index 33d3b9c2..0166446b 100644
--- a/vending-app/src/main/java/org/microg/vending/ui/InstallProgressNotification.kt
+++ b/vending-app/src/main/java/org/microg/vending/ui/InstallProgressNotification.kt
@@ -25,6 +25,7 @@ import org.microg.vending.enterprise.InstallError
import org.microg.vending.enterprise.InstallProgress
private const val INSTALL_NOTIFICATION_CHANNEL_ID = "packageInstall"
+private const val INSTALL_PROMPT_NOTIFICATION_CHANNEL_ID = "packageInstallPrompt"
internal fun Context.notifyInstallPrompt(packageName: String, sessionId: Int, installIntent: PendingIntent, deleteIntent: PendingIntent) {
val notificationManager = NotificationManagerCompat.from(this)
@@ -35,6 +36,7 @@ internal fun Context.notifyInstallPrompt(packageName: String, sessionId: Int, in
Log.e(TAG, "Couldn't load label for $packageName (${e.message}). Is it not installed?")
return
}
+ createPromptNotificationChannel()
getInstallPromptNotificationBuilder().apply {
setDeleteIntent(deleteIntent)
setContentTitle(getString(R.string.installer_notification_progress_splitinstall_click_install, label))
@@ -203,8 +205,9 @@ internal fun Context.notifyInstallProgress(
}
private fun Context.getInstallPromptNotificationBuilder() =
- NotificationCompat.Builder(this, INSTALL_NOTIFICATION_CHANNEL_ID)
- .setPriority(NotificationCompat.PRIORITY_LOW)
+ NotificationCompat.Builder(this, INSTALL_PROMPT_NOTIFICATION_CHANNEL_ID)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setCategory(NotificationCompat.CATEGORY_RECOMMENDATION)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLocalOnly(true)
@@ -232,6 +235,31 @@ private fun Context.createNotificationChannel() {
}
}
+internal fun Context.canShowInstallPrompt(): Boolean {
+ val notificationManager = NotificationManagerCompat.from(this)
+ if (!notificationManager.areNotificationsEnabled()) return false
+ if (android.os.Build.VERSION.SDK_INT < 26) return true
+ createPromptNotificationChannel()
+ return notificationManager.getNotificationChannel(INSTALL_PROMPT_NOTIFICATION_CHANNEL_ID)?.importance != NotificationManager.IMPORTANCE_NONE
+}
+
+// Install prompts need user action, so they use their own channel that can pop up.
+private fun Context.createPromptNotificationChannel() {
+ if (android.os.Build.VERSION.SDK_INT >= 26) {
+ val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ notificationManager.createNotificationChannel(
+ NotificationChannel(
+ INSTALL_PROMPT_NOTIFICATION_CHANNEL_ID,
+ getString(R.string.installer_notification_prompt_channel_name),
+ NotificationManager.IMPORTANCE_HIGH
+ ).apply {
+ description = getString(R.string.installer_notification_prompt_channel_description)
+ setShowBadge(false)
+ }
+ )
+ }
+}
+
private fun Context.isSystem(pm: PackageManager): Boolean {
try {
val ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
diff --git a/vending-app/src/main/kotlin/org/microg/vending/splitinstall/SplitInstallSessionManager.kt b/vending-app/src/main/kotlin/org/microg/vending/splitinstall/SplitInstallSessionManager.kt
index e09af2f3..07aee9a4 100644
--- a/vending-app/src/main/kotlin/org/microg/vending/splitinstall/SplitInstallSessionManager.kt
+++ b/vending-app/src/main/kotlin/org/microg/vending/splitinstall/SplitInstallSessionManager.kt
@@ -35,6 +35,7 @@ import kotlinx.coroutines.delay
import org.json.JSONArray
import org.json.JSONObject
import org.microg.vending.enterprise.Downloading
+import org.microg.vending.ui.canShowInstallPrompt
import org.microg.vending.ui.notifyInstallPrompt
import org.microg.vending.ui.notifySplitInstallProgress
import java.io.File
@@ -314,7 +315,11 @@ internal class SplitInstallSessionManager(
session.confirmationIntent = action.toUri(Intent.URI_INTENT_SCHEME)
val pendingIntent = confirmation(session) ?: return
try {
- update(session, STATUS_REQUIRES_USER_CONFIRMATION) { putParcelable("user_confirmation_intent", pendingIntent) }
+ // The heads-up notification asks for confirmation, so every app gets the same prompt.
+ // Without notifications, the app has to show the confirmation itself.
+ update(session, STATUS_REQUIRES_USER_CONFIRMATION, notifyClient = !context.canShowInstallPrompt()) {
+ putParcelable("user_confirmation_intent", pendingIntent)
+ }
} catch (e: SplitInstallException) {
update(session, STATUS_FAILED) { putInt("error_code", e.errorCode) }
abandonInstallerSession(session)
diff --git a/vending-app/src/main/res/values/strings.xml b/vending-app/src/main/res/values/strings.xml
index ea76fa2c..4a035e3c 100644
--- a/vending-app/src/main/res/values/strings.xml
+++ b/vending-app/src/main/res/values/strings.xml
@@ -46,6 +46,8 @@
<string name="installer_notification_channel_name">App and component installation</string>
<string name="installer_notification_channel_description">Shows app and component installation progress, success and failure messages.</string>
+ <string name="installer_notification_prompt_channel_name">Installation confirmation</string>
+ <string name="installer_notification_prompt_channel_description">Asks you to confirm app and component installations that need your approval.</string>
<string name="installer_notification_progress_downloading">Downloading \"%s\"</string>
<string name="installer_notification_progress_commiting">Installing \"%s\"</string>
<string name="installer_notification_progress_complete">Installed \"%s\"</string>
The same change as a patch on top of Logs (filtered, no account data): 3903-lang-huawei-splitcompat.log 3903-lang-huawei-splitcompat-no-conditions.log |
Known limitation: SplitCompat routing relies on Play Core version and install permissions, which do not reliably indicate client support.