Skip to content

Instantly share code, notes, and snippets.

@lkraav
Forked from raphendyr/sailfish_install_google_services.md
Last active February 9, 2026 21:09
Show Gist options
  • Select an option

  • Save lkraav/cde72215adfe0898f12798989f9606d8 to your computer and use it in GitHub Desktop.

Select an option

Save lkraav/cde72215adfe0898f12798989f9606d8 to your computer and use it in GitHub Desktop.
Google Play Services installation to Sailfish X
#!/bin/bash
#
# Install Google Apps into Sailfish OS AppSupport via bind-mounts.
# Based on raphendyr's guide, updated for SFOS 5.0 / Android 13.
#
# Instead of patching system.img, this populates /opt/gapps-for-appsupport/ and
# hooks into the AppSupport LXC container to bind-mount /product and
# /system/system_ext at startup. Survives SFOS updates; uninstall by
# removing the hook symlink.
#
set -e
GAPPS_DIR=/opt/gapps-for-appsupport
HOOK_DIR=/etc/appsupport.conf.d/init/prepare-hook.d
SYSTEM_IMG=/opt/appsupport/system.img
MINDTHEGAPPS_PATTERN='MindTheGapps-13.0.0-arm64-*.zip'
log() {
printf '%s\n' "$1" >&2
}
find_mindthegapps_zip() {
local downloads=/home/defaultuser/Downloads
log "Searching for $MINDTHEGAPPS_PATTERN in $downloads"
find "$downloads" -maxdepth 1 -name "$MINDTHEGAPPS_PATTERN" | sort | tail -n 1
}
install() {
local zip="$1"
local tmp
tmp="$(mktemp -d)"
# shellcheck disable=SC2064
trap "rm -rf '$tmp'" EXIT
log "Extracting MindTheGapps"
unzip -q -o "$zip" 'system/*' -d "$tmp"
# ── product ──────────────────────────────────────────────────
# Stock /product is empty; we own it entirely.
#
# Strip non-essential bloat (the three largest after GmsCore):
rm -rf "$tmp/system/product/priv-app/VelvetTitan"
rm -rf "$tmp/system/product/priv-app/Velvet"
rm -rf "$tmp/system/product/app/SpeechServicesByGoogle"
rm -rf "$tmp/system/product/app/talkback"
rm -rf "$tmp/system/product/app/MarkupGoogle"
rm -rf "$tmp/system/product/priv-app/AndroidAutoStub"
# addon.d is for Android recovery OTA survival, not needed here
rm -rf "$tmp/system/addon.d"
rm -rf "$GAPPS_DIR/product"
cp -a "$tmp/system/product" "$GAPPS_DIR/product"
# ── system_ext ───────────────────────────────────────────────
# Stock /system/system_ext has HAL libs, VINTF manifest, and
# build.prop that must be preserved. Our merge script copies
# stock first, then overlays MindTheGapps on top.
# Save GApps system_ext overlay for boot-time re-merge.
rm -rf "$GAPPS_DIR/system_ext.gapps"
cp -a "$tmp/system/system_ext" "$GAPPS_DIR/system_ext.gapps"
chown -R appsupport-root:appsupport-root "$GAPPS_DIR/system_ext.gapps"
# Initial merge (--force: no stamp yet).
"$GAPPS_DIR/merge-system-ext.sh" --force
# ── ownership ────────────────────────────────────────────────
chown -R appsupport-root:appsupport-root "$GAPPS_DIR/product"
rm -rf "$tmp"
trap - EXIT
}
deploy_merge_script() {
cat > "$GAPPS_DIR/merge-system-ext.sh" <<MERGE
#!/bin/sh
#
# Merge stock system_ext with GApps overlay.
# Skips work when system.img hasn't changed (stat-based stamp).
#
# Usage: merge-system-ext.sh [--force]
#
set -e
GAPPS_DIR=$GAPPS_DIR
SYSTEM_IMG=$SYSTEM_IMG
STAMP="\$GAPPS_DIR/.system_img.stamp"
force=false
[ "\${1-}" = "--force" ] && force=true
cur_stat=\$(stat -c '%s %Y' "\$SYSTEM_IMG")
old_stat=\$(cat "\$STAMP" 2>/dev/null || true)
if [ "\$force" = true ] || [ "\$cur_stat" != "\$old_stat" ]; then
echo "Merging stock system_ext with GApps overlay" >&2
tmp=\$(mktemp -d)
mount -o loop,ro "\$SYSTEM_IMG" "\$tmp"
rm -rf "\$GAPPS_DIR/system_ext"
cp -a "\$tmp/system/system_ext" "\$GAPPS_DIR/system_ext"
umount "\$tmp"
rmdir "\$tmp"
cp -a "\$GAPPS_DIR/system_ext.gapps/." "\$GAPPS_DIR/system_ext/"
chown -R appsupport-root:appsupport-root "\$GAPPS_DIR/system_ext"
echo "\$cur_stat" > "\$STAMP"
fi
MERGE
chmod +x "$GAPPS_DIR/merge-system-ext.sh"
}
create_hook() {
cat > "$GAPPS_DIR/prepare-hook.sh" <<EOF
#!/bin/sh
# Boot-time hook for GApps in AppSupport.
# Re-merges stock system_ext with GApps overlay only when system.img changes.
$GAPPS_DIR/merge-system-ext.sh
cat >> "\$CONTAINER_CONFIG_PATH/41-gapps_config" <<CONF
lxc.mount.entry = $GAPPS_DIR/product product none rbind,nodev,nosuid,ro,create=dir 0 0
lxc.mount.entry = $GAPPS_DIR/system_ext system/system_ext none rbind,nodev,nosuid,ro,create=dir 0 0
CONF
EOF
chmod +x "$GAPPS_DIR/prepare-hook.sh"
mkdir -p "$HOOK_DIR"
ln -sf "$GAPPS_DIR/prepare-hook.sh" \
"$HOOK_DIR/61-gapps-for-appsupport.sh"
}
# ── main ─────────────────────────────────────────────────────────
if [ "$(id -u)" -ne 0 ]; then
log "Run as root (devel-su)"
exit 1
fi
zip="$(find_mindthegapps_zip)"
if [ -z "$zip" ]; then
log "MindTheGapps zip not found. Download from https://wiki.lineageos.org/gapps"
exit 1
fi
log "Using $zip"
mkdir -p "$GAPPS_DIR"
systemctl stop appsupport@defaultuser 2>/dev/null || true
deploy_merge_script
install "$zip"
create_hook
log ""
log "Done. Start AppSupport — first boot will be slow (dex compilation)."
log "Monitor with: appsupport-attach /system/bin/logcat"
log ""
log "To uninstall:"
log " rm $HOOK_DIR/61-gapps-for-appsupport.sh # disable"
log " rm -rf $GAPPS_DIR # remove data"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment