mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
fix: ship the Ubisoft launcher service as an add-on pack, scan only the standard APK on main (#2436)
* fix: ship the Ubisoft launcher service as an add-on pack, not inside the APK * fix: treat the Ubisoft service as a packed tree, stage it when its pack lands * fix: serialize staging of the Ubisoft service
This commit is contained in:
1 parent
055e2ec78f
commit
dd9363e5f2
11 files changed
+63
-9
No files matched your search
@@ -900,6 +900,7 @@ jobs:
|
||||
uses: ./.github/workflows/scan.yml
|
||||
with:
|
||||
artifact: droiddeck-signed
|
||||
variant: standard
|
||||
secrets: inherit
|
||||
|
||||
packs:
|
||||
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
jq -r --arg n "$2" '.assets // [] | .[] | select(.name == $n) | .digest // empty'
|
||||
}
|
||||
commit=$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$BUILD_RUN" --jq .head_sha 2>/dev/null || echo "$GITHUB_SHA")
|
||||
for name in proot directaudio tracking xbox comet butler powervr; do
|
||||
for name in proot directaudio tracking xbox comet butler powervr ubisoft; do
|
||||
file="packs/$name.tar.zst"
|
||||
[ -f "$file" ] || { echo "::error::$name.tar.zst is missing from Build APK run $BUILD_RUN"; exit 1; }
|
||||
sha=$(sha256sum "$file" | cut -d' ' -f1)
|
||||
|
||||
@@ -7,6 +7,10 @@ on:
|
||||
description: Prefix of the signed artifacts to scan, as passed to sign.yml
|
||||
type: string
|
||||
required: true
|
||||
variant:
|
||||
description: Variant to scan, as listed in tools/release/variants.txt; every variant when omitted
|
||||
type: string
|
||||
default: '*'
|
||||
fail_on_detection:
|
||||
type: boolean
|
||||
default: false
|
||||
@@ -27,7 +31,7 @@ jobs:
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: ${{ inputs.artifact }}-*
|
||||
pattern: ${{ inputs.artifact }}-${{ inputs.variant }}
|
||||
merge-multiple: true
|
||||
path: scan
|
||||
|
||||
|
||||
+2
-2
@@ -93,7 +93,7 @@ def trackingPack = rootProject.file('tools/packs/tracking/release.env').readLine
|
||||
def prootPackPin = rootProject.file('tools/packs/proot/release.env').readLines()
|
||||
.findAll { it.contains('=') && !it.startsWith('#') }
|
||||
.collectEntries { def kv = it.split('=', 2); [(kv[0].trim()): kv[1].trim()] }
|
||||
def addonPacks = ['proot', 'directaudio', 'tracking', 'xbox', 'comet', 'butler', 'powervr'].collectEntries { name ->
|
||||
def addonPacks = ['proot', 'directaudio', 'tracking', 'xbox', 'comet', 'butler', 'powervr', 'ubisoft'].collectEntries { name ->
|
||||
def pack = file("build/packs/${name}.tar.zst")
|
||||
if (!pack.isFile()) {
|
||||
if (ciBuild) throw new GradleException("Add-on pack ${pack} is missing: run tools/packs/build.sh app/build/packs")
|
||||
@@ -205,7 +205,7 @@ android {
|
||||
}
|
||||
|
||||
androidResources {
|
||||
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~:<dir>droiddeck-xbox:<dir>droiddeck-comet:<dir>directaudio:<dir>hybris'
|
||||
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~:<dir>droiddeck-xbox:<dir>droiddeck-comet:<dir>droiddeck-ubisoft:<dir>directaudio:<dir>hybris'
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
|
||||
@@ -77,6 +77,8 @@ class App : Application() {
|
||||
if (com.droiddeck.launcher.stores.gog.GogComet.enabled(app)) com.droiddeck.launcher.core.AddonPack.COMET.ensure(app)
|
||||
if (com.droiddeck.launcher.stores.StoreAccounts.signedInAs(app, com.droiddeck.launcher.stores.Store.XBOX) != null)
|
||||
com.droiddeck.launcher.core.AddonPack.XBOX.ensure(app)
|
||||
if (com.droiddeck.launcher.stores.StoreAccounts.signedInAs(app, com.droiddeck.launcher.stores.Store.UBISOFT) != null)
|
||||
com.droiddeck.launcher.session.SessionFiles.ensureUbisoftService(app)
|
||||
com.droiddeck.launcher.handtracking.HandTracking.load(app)
|
||||
}.onFailure { android.util.Log.w("App", "add-on pack prefetch failed", it) }
|
||||
}, "addon-packs").start()
|
||||
|
||||
@@ -152,5 +152,7 @@ internal class AddonPack(
|
||||
@JvmField
|
||||
val POWERVR = AddonPack("powervr", BuildConfig.PACK_POWERVR_SHA256, BuildConfig.PACK_POWERVR_SIZE,
|
||||
listOf("runtime.tzst", "version.txt"))
|
||||
val UBISOFT = AddonPack("ubisoft", BuildConfig.PACK_UBISOFT_SHA256, BuildConfig.PACK_UBISOFT_SIZE,
|
||||
listOf("droiddeck-upc.exe"))
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,37 @@ object SessionFiles {
|
||||
"etc/xdg/autostart/droiddeck-clipboard.desktop" to "etc/xdg/autostart/droiddeck-clipboard.desktop",
|
||||
)
|
||||
|
||||
private const val UBISOFT_SERVICE = "usr/local/lib/droiddeck-ubisoft/droiddeck-upc.exe"
|
||||
|
||||
/** Fetches the Ubisoft pack and, once it lands, stages the service so a running session gets it without restarting. */
|
||||
fun ensureUbisoftService(context: Context) {
|
||||
AddonPack.UBISOFT.ensure(context) { app ->
|
||||
com.droiddeck.launcher.runtime.LinuxRuntime.rootDir(app).takeIf { it.isDirectory }
|
||||
?.let { stageUbisoftService(app, it, null) }
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun stageUbisoftService(context: Context, root: File, ledger: StageLedger?) {
|
||||
val pack = AddonPack.UBISOFT.takeIf { it.installed(context) }?.dir(context) ?: return
|
||||
val target = File(root, UBISOFT_SERVICE)
|
||||
if (ledger?.current(UBISOFT_SERVICE, target) == true) return
|
||||
val staged = File(target.parentFile, target.name + ".staged")
|
||||
var installed = false
|
||||
try {
|
||||
target.parentFile?.mkdirs()
|
||||
File(pack, "droiddeck-upc.exe").inputStream().use { input ->
|
||||
staged.outputStream().use { output -> FileUtils.copy(input, output) }
|
||||
}
|
||||
installed = staged.setExecutable(true, false) && staged.renameTo(target)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "could not stage the Ubisoft launcher service", e)
|
||||
} finally {
|
||||
if (!installed) staged.delete()
|
||||
}
|
||||
if (installed) ledger?.staged(UBISOFT_SERVICE, target) else Log.e(TAG, "$UBISOFT_SERVICE NOT staged")
|
||||
}
|
||||
|
||||
fun stage(context: Context, root: File, startsSession: Boolean = false) {
|
||||
if (DesktopCatalog.desktopInstalled(root) && !DesktopCatalog.desktopFromPack(root)) try { DesktopCatalog.cleanLegacy(context) }
|
||||
catch (e: Exception) { Log.w(TAG, "LXQt cleanup will retry next session", e) }
|
||||
@@ -196,7 +227,6 @@ object SessionFiles {
|
||||
"usr/local/lib/droiddeck-pipewire.tzst" to "usr/local/lib/droiddeck-pipewire.tzst",
|
||||
"usr/local/share/licenses/uruntime/LICENSE" to "usr/local/share/licenses/uruntime/LICENSE",
|
||||
"usr/local/lib/droiddeck-dirt/ws2_32.dll" to "usr/local/lib/droiddeck-dirt/ws2_32.dll",
|
||||
"usr/local/lib/droiddeck-ubisoft/droiddeck-upc.exe" to "usr/local/lib/droiddeck-ubisoft/droiddeck-upc.exe",
|
||||
) + mangoapp + xclipse.map { it to it } + maliCompat + msitools.map { it to it } + vr + gpuLoad + fexPreloads).filter { (asset, _) ->
|
||||
val dir = asset.substringBeforeLast('/')
|
||||
runCatching { context.assets.list("linuxfs/$dir")?.contains(asset.substringAfterLast('/')) == true }.getOrDefault(false)
|
||||
@@ -252,6 +282,7 @@ object SessionFiles {
|
||||
}
|
||||
if (installed) ledger.staged(key, target) else Log.e(TAG, "DirectAudio $set/$relative NOT staged")
|
||||
}
|
||||
stageUbisoftService(context, root, ledger)
|
||||
ledger.save()
|
||||
for (old in arrayOf("aarch64-unix", "aarch64-windows", "i386-windows")) File(root, "$DIRECTAUDIO_DIR/lib/wine/$old").deleteRecursively()
|
||||
val preload = StringBuilder("/usr/local/lib/libblsession.so\n")
|
||||
|
||||
@@ -132,6 +132,7 @@ object UbisoftAuth {
|
||||
} ?: remember.takeIf { it.isNotEmpty() }?.let { rm -> parseSession(session("rm_v1 t=$rm").body, rm) }
|
||||
?: return "no ticket"
|
||||
save(context, fresh)
|
||||
com.droiddeck.launcher.session.SessionFiles.ensureUbisoftService(context)
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ pack tracking "$work/tracking" hand_landmarker.task hand_landmarker_lite.task po
|
||||
pack xbox "$linuxfs/droiddeck-xbox" version xodus-cli xodus-service
|
||||
pack comet "$linuxfs/droiddeck-comet" version comet
|
||||
pack powervr "$repo/app/src/main/assets/hybris" runtime.tzst version.txt
|
||||
[ ! -f "$linuxfs/droiddeck-ubisoft/droiddeck-upc.exe" ] || pack ubisoft "$linuxfs/droiddeck-ubisoft" droiddeck-upc.exe
|
||||
|
||||
mkdir "$work/butler"
|
||||
fetch "https://broth.itch.zone/butler/linux-arm64/${BUTLER_VERSION}/archive/default" \
|
||||
|
||||
@@ -14,15 +14,16 @@ ROOT = Path(__file__).resolve().parents[2]
|
||||
MANIFEST = Path('app/build/runtime-inputs.json')
|
||||
PACKS = Path('app/build/packs')
|
||||
PACKED = {'assets/linuxfs/usr/local/lib/droiddeck-xbox/': 'xbox', 'assets/linuxfs/usr/local/lib/droiddeck-comet/': 'comet',
|
||||
'assets/linuxfs/usr/local/lib/droiddeck-ubisoft/': 'ubisoft',
|
||||
'assets/directaudio/': 'directaudio', 'assets/hybris/': 'powervr'}
|
||||
PACKED_FILES = {'lib/arm64-v8a/libproot.so': ('proot', 'libproot.so'),
|
||||
'lib/arm64-v8a/libproot-loader.so': ('proot', 'libproot-loader.so')}
|
||||
PACKED_LIBRARIES = ['lib/arm64-v8a/libopencv_java4.so', 'lib/arm64-v8a/libmediapipe_tasks_jni.so']
|
||||
PACK_NAMES = ['proot', 'directaudio', 'tracking', 'xbox', 'comet', 'butler', 'powervr']
|
||||
PACK_NAMES = ['proot', 'directaudio', 'tracking', 'xbox', 'comet', 'butler', 'powervr', 'ubisoft']
|
||||
SOURCE_PATHS = ['app/build.gradle', 'app/src/main/cpp/fakeinput_steam.cpp', 'app/src/main/assets/pulseaudio.tzst',
|
||||
'tools/linuxfs', 'tools/proot', 'tools/aaudio-sink', 'tools/directaudio', 'tools/packs',
|
||||
'tools/gamescope/release.env', 'tools/droiddeck-esync/release.env',
|
||||
'tools/mangoapp', 'tools/msitools', 'tools/pipewire', 'tools/comet', 'tools/xbox', 'tools/dirt-ws2', 'tools/components', 'tools/ci', 'tools/build_local.sh',
|
||||
'tools/mangoapp', 'tools/msitools', 'tools/pipewire', 'tools/comet', 'tools/xbox', 'tools/dirt-ws2', 'tools/ubisoft-launcher', 'tools/components', 'tools/ci', 'tools/build_local.sh',
|
||||
'tools/powervr', 'tools/hybris', 'tools/dxvk', 'tools/vulkan-wsi',
|
||||
'tools/release/runtime_inputs.py', 'app/src/main/assets/desktop-removal',
|
||||
'tools/quest', 'tools/vr/src', 'tools/vr/driver_droiddeck', 'tools/vr/layer', 'tools/vr/build.sh',
|
||||
@@ -211,7 +212,8 @@ def pack_digests(root, name):
|
||||
|
||||
def check_apk(apk, root=ROOT):
|
||||
data = verify(root)
|
||||
packs = {name: pack_digests(root, name) for name in PACK_NAMES}
|
||||
packs = {name: pack_digests(root, name) for name in PACK_NAMES
|
||||
if name != 'ubisoft' or (root / PACKS / 'ubisoft.tar.zst').is_file()}
|
||||
with ZipFile(apk) as package:
|
||||
names = set(package.namelist())
|
||||
for library in PACKED_LIBRARIES:
|
||||
@@ -231,7 +233,7 @@ def check_apk(apk, root=ROOT):
|
||||
if prefix:
|
||||
if name in names:
|
||||
raise ValueError('APK still carries ' + name + ', which the ' + PACKED[prefix] + ' pack provides')
|
||||
if packs[PACKED[prefix]].get(name[len(prefix):]) != output['sha256']:
|
||||
if packs.get(PACKED[prefix], {}).get(name[len(prefix):]) != output['sha256']:
|
||||
raise ValueError('Add-on pack ' + PACKED[prefix] + ' is missing or has a stale ' + name)
|
||||
continue
|
||||
try:
|
||||
|
||||
@@ -33,6 +33,9 @@ class RuntimeInputsTest(unittest.TestCase):
|
||||
self.source = self.root / 'tools/linuxfs/preload/test.c'
|
||||
self.source.parent.mkdir(parents=True)
|
||||
self.source.write_text('current source')
|
||||
service = self.root / 'app/src/main/assets/linuxfs/usr/local/lib/droiddeck-ubisoft/droiddeck-upc.exe'
|
||||
service.parent.mkdir(parents=True)
|
||||
service.write_bytes(b'MZ service')
|
||||
self.audio(['pactl', 'modules/arm64/module-aaudio-sink.so', 'modules/arm64/module-directaudio-native-sink.so'])
|
||||
self.packs()
|
||||
|
||||
@@ -179,6 +182,13 @@ class RuntimeInputsTest(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError, 'xbox pack provides'):
|
||||
runtime.check_apk(apk, self.root)
|
||||
|
||||
def test_an_apk_still_carrying_the_ubisoft_service_is_rejected(self):
|
||||
name = 'assets/linuxfs/usr/local/lib/droiddeck-ubisoft/droiddeck-upc.exe'
|
||||
self.assertIn(name, runtime.outputs(self.root))
|
||||
apk = self.current_apk(self.root / 'bundled.apk', {name: b'MZ service'})
|
||||
with self.assertRaisesRegex(ValueError, 'ubisoft pack provides'):
|
||||
runtime.check_apk(apk, self.root)
|
||||
|
||||
def test_an_apk_still_carrying_the_powervr_runtime_is_rejected(self):
|
||||
apk = self.current_apk(self.root / 'bundled.apk', {'assets/hybris/runtime.tzst': b'runtime'})
|
||||
with self.assertRaisesRegex(ValueError, 'powervr pack provides'):
|
||||
|
||||
Reference in new issue
Block a user