diff --git a/vending-app/build.gradle b/vending-app/build.gradle
index 12efd905a408e3926d20e38b3aca30adad22db94..2ccb1cbe6043faa46d3f8f2ffad1cc672a5e1fcb 100644
--- a/vending-app/build.gradle
+++ b/vending-app/build.gradle
@@ -6,6 +6,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.squareup.wire'
+apply plugin: 'org.jetbrains.kotlin.android'
android {
namespace "com.android.vending"
@@ -64,6 +65,8 @@ dependencies {
implementation "com.squareup.wire:wire-runtime:$wireVersion"
implementation "com.android.volley:volley:$volleyVersion"
+ implementation 'androidx.core:core-ktx:1.0.0'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
}
wire {
@@ -80,4 +83,4 @@ android.applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFileName = variant.applicationId + "-" + variant.versionCode + variant.versionName.substring(vendingAppVersionName.length()) + ".apk"
}
-}
\ No newline at end of file
+}
diff --git a/vending-app/src/main/AndroidManifest.xml b/vending-app/src/main/AndroidManifest.xml
index 4763d05df783280f99a0dc3f279ecf6b6bfc919a..dc7fce16e651cf6b3a90d4a7a127c26edc7eb0a4 100644
--- a/vending-app/src/main/AndroidManifest.xml
+++ b/vending-app/src/main/AndroidManifest.xml
@@ -53,6 +53,14 @@
+
+
+
+
+
+
list, in Bundle bundle, in ISplitInstallServiceCallback callback);
+
+ // Method not identified yet
+ void c(String str);
+
+ // Method not identified yet
+ void d(String str);
+
+ // Method not identified yet
+ void e(String str);
+
+ void getSessionStates(String str, in ISplitInstallServiceCallback callback);
+
+}
\ No newline at end of file
diff --git a/vending-app/src/main/aidl/com/google/android/play/core/splitinstall/protocol/ISplitInstallServiceCallback.aidl b/vending-app/src/main/aidl/com/google/android/play/core/splitinstall/protocol/ISplitInstallServiceCallback.aidl
new file mode 100644
index 0000000000000000000000000000000000000000..371ab555fad4c02bf4f348265b08ecd6c98f159c
--- /dev/null
+++ b/vending-app/src/main/aidl/com/google/android/play/core/splitinstall/protocol/ISplitInstallServiceCallback.aidl
@@ -0,0 +1,37 @@
+/*
+ * SPDX-FileCopyrightText: 2016 microG Project Team
+ * SPDX-FileCopyrightText: 2023 E FOUNDATION
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.google.android.play.core.splitinstall.protocol;
+
+import android.os.Bundle;
+
+interface ISplitInstallServiceCallback {
+
+ // Method not identified yet
+ void a();
+
+ void onStartInstall(int i);
+
+ void onCompleteInstall(int i);
+
+ void onCancelInstall(int i);
+
+ void onGetSession(int i);
+
+ void onError(int i);
+
+ void onGetSessionStates(in List list);
+
+ void onDeferredUninstall(in Bundle bundle);
+
+ void onDeferredInstall(in Bundle bundle);
+
+ void onGetSplitsForAppUpdate(int i, in Bundle bundle);
+
+ void onCompleteInstallForAppUpdate();
+
+ void onDeferredLanguageInstall(int i);
+}
\ No newline at end of file
diff --git a/vending-app/src/main/aidl/foundation/e/apps/ISplitInstallService.aidl b/vending-app/src/main/aidl/foundation/e/apps/ISplitInstallService.aidl
new file mode 100644
index 0000000000000000000000000000000000000000..dd57f65d20e22cb57fd47c98ae5bcc465672020a
--- /dev/null
+++ b/vending-app/src/main/aidl/foundation/e/apps/ISplitInstallService.aidl
@@ -0,0 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: 2016 microG Project Team
+ * SPDX-FileCopyrightText: 2023 E FOUNDATION
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package foundation.e.apps;
+
+interface ISplitInstallService {
+ void installSplitModule(String packageName, String moduleName);
+}
diff --git a/vending-app/src/main/java/com/android/vending/SplitInstallService.kt b/vending-app/src/main/java/com/android/vending/SplitInstallService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..05570e37f1a5352441958720bb57311de928355b
--- /dev/null
+++ b/vending-app/src/main/java/com/android/vending/SplitInstallService.kt
@@ -0,0 +1,100 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-FileCopyrightText: 2023 E FOUNDATION
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.android.vending
+
+import android.app.Service
+import android.content.Intent
+import android.os.Bundle
+import android.os.IBinder
+import android.os.Parcel
+import android.util.Log
+import com.android.vending.splitinstall.SplitInstaller
+import com.android.vending.splitinstall.SplitInstallerFactory
+import com.android.vending.splitinstall.SplitInstallerType
+import com.google.android.play.core.splitinstall.protocol.ISplitInstallService
+import com.google.android.play.core.splitinstall.protocol.ISplitInstallServiceCallback
+
+class SplitInstallService : Service() {
+
+ companion object {
+ const val TAG = "SplitInstallService"
+ }
+
+ private lateinit var mSplitInstaller: SplitInstaller
+
+ override fun onCreate() {
+ super.onCreate()
+
+ mSplitInstaller = SplitInstallerFactory.createSplitInstaller(
+ applicationContext,
+ SplitInstallerType.AppLoungeSplitInstaller
+ )
+
+ mSplitInstaller.initialize()
+ }
+
+ override fun onBind(p0: Intent?): IBinder {
+ return mServiceInterface
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ mSplitInstaller.destroy()
+ }
+
+ private var mServiceInterface = object : ISplitInstallService.Stub() {
+
+ override fun a() {
+ Log.d(TAG, "a")
+ }
+
+ override fun startInstall(
+ packageName: String,
+ list: List,
+ bundle: Bundle,
+ callback: ISplitInstallServiceCallback
+ ) {
+ for (element in list) {
+
+ val apk = element.getString("module_name") ?: element.getString("language")
+ apk?.let {
+ mSplitInstaller.install(packageName, apk)
+ callback.onStartInstall(0)
+ } ?: logBundleError(element)
+ }
+ }
+
+ private fun logBundleError(bundle: Bundle) {
+ for (entry in bundle.keySet()) {
+ Log.e(TAG, "Unknown bundle entry: $entry. Value is ${bundle.get(entry)}")
+ }
+ }
+
+ override fun c(str: String?) {
+ Log.d(TAG, "c")
+ }
+
+ override fun d(str: String?) {
+ Log.d(TAG, "d")
+ }
+
+ override fun e(str: String?) {
+ Log.d(TAG, "e")
+ }
+
+ override fun getSessionStates(str: String, callback: ISplitInstallServiceCallback) {
+ Log.d(TAG, "onGetSessionStates")
+ callback.onGetSessionStates(arrayListOf())
+ }
+
+ override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
+ if (super.onTransact(code, data, reply, flags)) return true
+ Log.d(TAG, "onTransact [unknown]: $code, $data, $flags")
+ return false
+ }
+ }
+}
diff --git a/vending-app/src/main/java/com/android/vending/splitinstall/SplitInstallErrorCode.kt b/vending-app/src/main/java/com/android/vending/splitinstall/SplitInstallErrorCode.kt
new file mode 100644
index 0000000000000000000000000000000000000000..24edcda6b6da170439d87a3611bae74000d3aa56
--- /dev/null
+++ b/vending-app/src/main/java/com/android/vending/splitinstall/SplitInstallErrorCode.kt
@@ -0,0 +1,13 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-FileCopyrightText: 2023 E FOUNDATION
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.android.vending.splitinstall
+
+interface SplitInstallErrorCode {
+ companion object {
+ const val API_NOT_AVAILABLE = -5
+ }
+}
diff --git a/vending-app/src/main/java/com/android/vending/splitinstall/SplitInstaller.kt b/vending-app/src/main/java/com/android/vending/splitinstall/SplitInstaller.kt
new file mode 100644
index 0000000000000000000000000000000000000000..91c531db2852fb129150377451d8ca61c4f90b67
--- /dev/null
+++ b/vending-app/src/main/java/com/android/vending/splitinstall/SplitInstaller.kt
@@ -0,0 +1,30 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-FileCopyrightText: 2023 E FOUNDATION
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.android.vending.splitinstall
+
+import android.content.Context
+import com.android.vending.splitinstall.installer.AppLoungeSplitInstaller
+
+interface SplitInstaller {
+ fun initialize()
+ fun install(packageName: String, moduleName: String)
+ fun destroy()
+}
+
+enum class SplitInstallerType {
+ AppLoungeSplitInstaller
+}
+
+object SplitInstallerFactory {
+ fun createSplitInstaller(context: Context, type: SplitInstallerType): SplitInstaller {
+ when (type) {
+ SplitInstallerType.AppLoungeSplitInstaller ->
+ return AppLoungeSplitInstaller(context)
+ else -> throw IllegalArgumentException("Unknown SplitInstallerType")
+ }
+ }
+}
diff --git a/vending-app/src/main/java/com/android/vending/splitinstall/installer/AppLoungeSplitInstaller.kt b/vending-app/src/main/java/com/android/vending/splitinstall/installer/AppLoungeSplitInstaller.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1c01af1ee276266713e956888d6b2af4b0d66141
--- /dev/null
+++ b/vending-app/src/main/java/com/android/vending/splitinstall/installer/AppLoungeSplitInstaller.kt
@@ -0,0 +1,73 @@
+/*
+ * SPDX-FileCopyrightText: 2023 microG Project Team
+ * SPDX-FileCopyrightText: 2023 E FOUNDATION
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.android.vending.splitinstall.installer
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.IBinder
+import com.android.vending.splitinstall.SplitInstaller
+import foundation.e.apps.ISplitInstallService
+
+class AppLoungeSplitInstaller(
+ private val context: Context
+) : SplitInstaller {
+
+ companion object {
+ private val ON_DEMAND_DELIVERY_SERVICE_COMPONENT =
+ ComponentName(
+ "foundation.e.apps",
+ "foundation.e.apps.install.splitinstall.SplitInstallService"
+ )
+ }
+
+ private var service: ISplitInstallService? = null
+ private val moduleList = ArrayList()
+
+ private val serviceConnection = object : ServiceConnection {
+ override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
+ service = ISplitInstallService.Stub.asInterface(binder)
+ installWaitingModules()
+ }
+
+ override fun onServiceDisconnected(componentName: ComponentName) {
+ service = null
+ }
+ }
+
+ override fun initialize() {
+ val intent = Intent().apply {
+ component = ON_DEMAND_DELIVERY_SERVICE_COMPONENT
+ }
+
+ context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
+ }
+
+ override fun destroy() {
+ context.unbindService(serviceConnection)
+ }
+
+ override fun install(packageName: String, moduleName: String) {
+ if (service == null) {
+ moduleList.add(InstallModule(packageName, moduleName))
+ }
+
+ service?.installSplitModule(packageName, moduleName)
+ }
+
+ private fun installWaitingModules() {
+ val iterator = moduleList.iterator()
+ while (iterator.hasNext()) {
+ val module = iterator.next()
+ service?.installSplitModule(module.packageName, module.moduleName)
+ iterator.remove()
+ }
+ }
+
+ private data class InstallModule(val packageName: String, val moduleName: String)
+}