mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
App name, app ID and code package: DroidDeck, com.droiddeck.launcher (sources moved to com/droiddeck/launcher, the 68 JNI functions renamed with them). The new app ID installs beside SteamDeck rather than over it. Everywhere the name was the app's own: the sidebar title, the session report, share zips, the shell title, wake-lock and log tags, User-Agents, the Compose theme, the Download folder and switch files (droiddeck-env, -osc, -driver, -tu-debug, -no-pad, -pad-log, -no-hud, -wlr-renderer; the old names are not read), and inside the runtime (droiddeck-desktop, Firefox's droiddeck.js, the shortcuts tag, the rumble socket, debug and cache paths). Build scripts, CI artifact (droiddeck-apk), CMake project, README and the progress log follow. Kept on purpose: Valve's Steam Deck (the -steamdeck flag, steamdeck_publicbeta/stable, 'Steam Deck mode', the steamos.cloud package URLs) and names inside the hosted desktop package (steamdeck.png, steamdeck-steam, steamdeck-bigpicture). 'Desktop installed' now tests for labwc instead of the launcher the app stages itself. A committed __pycache__ .pyc is removed.
219 lines
9.0 KiB
Bash
Executable File
219 lines
9.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
repo_root=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
|
sdk_dir=${ANDROID_HOME:-${ANDROID_SDK_ROOT:-"${HOME}/Library/Android/sdk"}}
|
|
java_dir=${JAVA_HOME:-"/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home"}
|
|
image_name=${DROIDDECK_BUILD_IMAGE:-droiddeck-local-cross:24.04-v2}
|
|
|
|
if [[ ! -x "${sdk_dir}/platform-tools/adb" ]]; then
|
|
echo "Android SDK not found at ${sdk_dir}; set ANDROID_HOME or ANDROID_SDK_ROOT." >&2
|
|
exit 1
|
|
fi
|
|
if [[ ! -x "${java_dir}/bin/java" ]]; then
|
|
echo "Java 17 not found at ${java_dir}; set JAVA_HOME." >&2
|
|
exit 1
|
|
fi
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo "Docker is required to cross-compile the glibc ARM64 preload libraries." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker image inspect "${image_name}" >/dev/null 2>&1; then
|
|
docker build --platform linux/amd64 -t "${image_name}" \
|
|
-f "${repo_root}/tools/local-cross.Dockerfile" "${repo_root}"
|
|
fi
|
|
|
|
docker run --rm --platform linux/amd64 \
|
|
--user "$(id -u):$(id -g)" \
|
|
-v "${repo_root}:/src" -w /src "${image_name}" bash -lc '
|
|
set -euo pipefail
|
|
d=app/src/main/assets/linuxfs
|
|
mkdir -p "$d"
|
|
aarch64-linux-gnu-g++ -shared -fPIC -O2 -Wall -Wno-attributes -Wno-nonnull-compare \
|
|
-pthread -std=c++17 -static-libstdc++ -static-libgcc \
|
|
-o "$d/libfakeinput.so" app/src/main/cpp/fakeinput_steam.cpp -ldl
|
|
aarch64-linux-gnu-strip --strip-unneeded "$d/libfakeinput.so"
|
|
aarch64-linux-gnu-gcc -shared -fPIC -O2 -Wall -pthread \
|
|
-o "$d/libblsession.so" tools/linuxfs/preload/*.c -ldl
|
|
aarch64-linux-gnu-strip --strip-unneeded "$d/libblsession.so"
|
|
for script in tools/linuxfs/overlay/usr/local/bin/bannerlator-*; do
|
|
install -Dm644 "$script" "$d/usr/local/bin/$(basename "$script")"
|
|
done
|
|
install -Dm644 tools/linuxfs/desktop/droiddeck-desktop "$d/usr/local/bin/droiddeck-desktop"
|
|
install -Dm644 tools/linuxfs/desktop/autostart "$d/etc/xdg/labwc/autostart"
|
|
install -Dm644 tools/linuxfs/desktop/rc.xml "$d/etc/xdg/labwc/rc.xml"
|
|
install -Dm644 tools/linuxfs/desktop/panel.conf "$d/etc/xdg/lxqt/panel.conf"
|
|
install -Dm644 tools/linuxfs/desktop/firefox-droiddeck.js \
|
|
"$d/usr/lib/firefox/defaults/pref/droiddeck.js"
|
|
|
|
need=$(aarch64-linux-gnu-readelf -d "$d/libfakeinput.so" | sed -n "s/.*NEEDED.*\\[\\(.*\\)\\]/\\1/p")
|
|
for bad in libstdc++.so.6 libgcc_s.so.1; do
|
|
if printf "%s\\n" "$need" | grep -qx "$bad"; then
|
|
echo "libfakeinput.so links $bad; the C++ runtime must stay static" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
syms() { aarch64-linux-gnu-readelf -Ws "$1" | awk '\''$4 == "FUNC" && $5 == "GLOBAL" {sub(/@.*/, "", $8); print $8}'\''; }
|
|
fake=$(syms "$d/libfakeinput.so")
|
|
for sym in open openat ioctl read close poll ppoll select stat fstat access scandir; do
|
|
printf "%s\\n" "$fake" | grep -qx "$sym" || {
|
|
echo "libfakeinput.so does not export $sym" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
session=$(syms "$d/libblsession.so")
|
|
for sym in socket bind getsockname setsockopt statfs statvfs; do
|
|
printf "%s\\n" "$session" | grep -qx "$sym" || {
|
|
echo "libblsession.so does not export $sym" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
test -f "$d/usr/local/bin/bannerlator-session"
|
|
test -f "$d/usr/local/bin/bannerlator-proton-extra"
|
|
'
|
|
|
|
export ANDROID_HOME="${sdk_dir}"
|
|
export ANDROID_SDK_ROOT="${sdk_dir}"
|
|
export JAVA_HOME="${java_dir}"
|
|
ndk_version=${DROIDDECK_NDK_VERSION:-}
|
|
if [[ -z "${ndk_version}" ]]; then
|
|
ndk_path=$(find "${sdk_dir}/ndk" -mindepth 1 -maxdepth 1 -type d -print | sort -V | tail -1)
|
|
ndk_version=${ndk_path##*/}
|
|
fi
|
|
if [[ -z "${ndk_version}" || ! -d "${sdk_dir}/ndk/${ndk_version}" ]]; then
|
|
echo "No Android NDK found under ${sdk_dir}/ndk; set DROIDDECK_NDK_VERSION." >&2
|
|
exit 1
|
|
fi
|
|
|
|
for tool in curl tar zstd; do
|
|
if ! command -v "${tool}" >/dev/null 2>&1; then
|
|
echo "${tool} is required to build the bundled audio modules." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
staging_dir=$(mktemp -d "${TMPDIR:-/tmp}/droiddeck-build.XXXXXX")
|
|
bundle_asset="${repo_root}/app/src/main/assets/pulseaudio.tzst"
|
|
bundle_backup="${staging_dir}/pulseaudio.original.tzst"
|
|
bundle_replaced=0
|
|
cleanup() {
|
|
local exit_code=$?
|
|
trap - EXIT
|
|
if [[ "${bundle_replaced}" == 1 ]]; then
|
|
cp -p "${bundle_backup}" "${bundle_asset}" || exit_code=1
|
|
fi
|
|
rm -rf -- "${staging_dir}" || exit_code=1
|
|
exit "${exit_code}"
|
|
}
|
|
trap cleanup EXIT
|
|
trap 'exit 130' INT
|
|
trap 'exit 143' TERM
|
|
|
|
pa_source=${DROIDDECK_PA13_SOURCE_DIR:-"${staging_dir}/pulseaudio-13.0"}
|
|
if [[ -z "${DROIDDECK_PA13_SOURCE_DIR:-}" ]]; then
|
|
curl -fsSL -o "${staging_dir}/pulseaudio-13.0.tar.gz" \
|
|
https://github.com/pulseaudio/pulseaudio/archive/refs/tags/v13.0.tar.gz
|
|
mkdir -p "${pa_source}"
|
|
tar -xzf "${staging_dir}/pulseaudio-13.0.tar.gz" \
|
|
-C "${pa_source}" --strip-components=1
|
|
fi
|
|
if [[ ! -f "${pa_source}/src/pulse/version.h.in" ]]; then
|
|
echo "PulseAudio 13.0 source not found at ${pa_source}; set DROIDDECK_PA13_SOURCE_DIR." >&2
|
|
exit 1
|
|
fi
|
|
|
|
export NDK="${sdk_dir}/ndk/${ndk_version}"
|
|
sink_output="${staging_dir}/sink-out"
|
|
"${repo_root}/tools/aaudio-sink/build.sh" "${pa_source}" "${sink_output}"
|
|
|
|
cp -p "${bundle_asset}" "${bundle_backup}"
|
|
bundle_dir="${staging_dir}/pulseaudio-bundle"
|
|
mkdir -p "${bundle_dir}"
|
|
zstd -dc "${bundle_asset}" | tar -xf - -C "${bundle_dir}"
|
|
if [[ -e "${bundle_dir}/modules/arm64/module-aaudio-sink.so" \
|
|
|| -e "${bundle_dir}/modules/arm64/module-directaudio-sink.so" ]]; then
|
|
echo "The committed audio bundle already contains a built ARM64 sink." >&2
|
|
exit 1
|
|
fi
|
|
install -m755 "${sink_output}/module-aaudio-sink.so" \
|
|
"${bundle_dir}/modules/arm64/module-aaudio-sink.so"
|
|
install -m755 "${sink_output}/module-directaudio-sink.so" \
|
|
"${bundle_dir}/modules/arm64/module-directaudio-sink.so"
|
|
tar -cf - -C "${bundle_dir}" . | zstd -19 -c > "${staging_dir}/pulseaudio.tzst"
|
|
bundle_replaced=1
|
|
mv "${staging_dir}/pulseaudio.tzst" "${bundle_asset}"
|
|
|
|
cd "${repo_root}"
|
|
./gradlew assembleRelease -PndkVersion="${ndk_version}"
|
|
cp -p "${bundle_backup}" "${bundle_asset}"
|
|
bundle_replaced=0
|
|
|
|
apk="${repo_root}/app/build/outputs/apk/release/app-release.apk"
|
|
audio_check="${staging_dir}/audio-check"
|
|
mkdir -p "${audio_check}"
|
|
unzip -p "${apk}" assets/pulseaudio.tzst | zstd -dc | tar -xf - -C "${audio_check}"
|
|
for audio_file in \
|
|
pactl \
|
|
modules/arm64/module-aaudio-sink.so \
|
|
modules/arm64/module-directaudio-sink.so; do
|
|
if [[ ! -f "${audio_check}/${audio_file}" ]]; then
|
|
echo "APK audio bundle is missing ${audio_file}." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
build_tools=$(find "${sdk_dir}/build-tools" -mindepth 1 -maxdepth 1 -type d -print | sort -V | tail -1)
|
|
if [[ ! -x "${build_tools}/zipalign" || ! -x "${build_tools}/apksigner" ]]; then
|
|
echo "Android build-tools with zipalign/apksigner are required under ${sdk_dir}/build-tools." >&2
|
|
exit 1
|
|
fi
|
|
|
|
"${build_tools}/zipalign" -p -f 4 "${apk}" "${staging_dir}/app-release.aligned.apk"
|
|
"${build_tools}/apksigner" sign \
|
|
--ks keystore/testkey.p12 --ks-type PKCS12 --ks-pass pass:android \
|
|
--ks-key-alias testkey --key-pass pass:android \
|
|
--v1-signing-enabled true --v2-signing-enabled true --v3-signing-enabled true \
|
|
--out "${apk}" "${staging_dir}/app-release.aligned.apk"
|
|
|
|
signature_output=$("${build_tools}/apksigner" verify --min-sdk-version 21 --verbose --print-certs "${apk}")
|
|
printf '%s\n' "${signature_output}"
|
|
for scheme in \
|
|
'Verified using v1 scheme (JAR signing): true' \
|
|
'Verified using v2 scheme (APK Signature Scheme v2): true' \
|
|
'Verified using v3 scheme (APK Signature Scheme v3): true'; do
|
|
grep -qF "${scheme}" <<<"${signature_output}" || {
|
|
echo "APK signature check failed: ${scheme}" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
grep -qF 'Signer #1 certificate DN: EMAILADDRESS=android@android.com, CN=Android, OU=Android, O=Android' \
|
|
<<<"${signature_output}"
|
|
|
|
docker run --rm --platform linux/amd64 -v "${repo_root}:/src:ro" -w /src "${image_name}" \
|
|
bash -lc '
|
|
set -euo pipefail
|
|
apk=app/build/outputs/apk/release/app-release.apk
|
|
work=$(mktemp -d)
|
|
unzip -q "$apk" "lib/arm64-v8a/*" -d "$work"
|
|
cd "$work/lib/arm64-v8a"
|
|
system="libc.so libm.so libdl.so liblog.so libandroid.so libz.so libvulkan.so
|
|
libGLESv2.so libEGL.so libnativewindow.so libjnigraphics.so libaaudio.so
|
|
libOpenSLES.so libmediandk.so libcamera2ndk.so libsync.so libneuralnetworks.so"
|
|
fail=0
|
|
for so in *.so; do
|
|
for need in $(readelf -d "$so" | sed -n "s/.*NEEDED.*\\[\\(.*\\)\\]/\\1/p"); do
|
|
[ -f "$need" ] && continue
|
|
case " $(echo $system) " in *" $need "*) continue ;; esac
|
|
echo "missing: $so -> $need"
|
|
fail=1
|
|
done
|
|
done
|
|
[ "$fail" -eq 0 ]
|
|
echo "every NEEDED resolves"
|
|
'
|
|
|
|
printf 'APK: %s\n' "${apk}"
|
|
printf 'SHA-256: '
|
|
shasum -a 256 "${apk}" | awk '{print $1}'
|