Baue dein eigenes Android-GSI

Seit Android 8 haben (fast) alle Android Smartphones und Tablets eine (fast) einheitliche Ebene, auf der das Betriebssystem aufsetzt (vereinfacht).
Die Hersteller müssen seitdem ihre Hardware-spezifischen Komponenten selbst liefern. Früher musste Google für jedes Gerät ein eigenes System-Image entwickeln.
Es entstand das Project Treble und viele verschiedene Arten Android (GSI), basierend auf AOSP (Android Open Source Projekt), von dem auch Google sein System bezieht.

Ein Generic System Image (kurz GSI) ist ein Android-System-Image, dass theoretisch auf jedem Project Treble unterstützten Gerät verwendet werden kann.

Voraussetzung für die Installation eines GSI-Images auf dem Handy sind:

  • Das Gerät muss Treble-fähig sein
  • OEM muss entsperrt sein
  • Das Recovery* muss ausgetauscht sein
    * Die vom Hersteller installierten Revovery-Versionen erlauben das Flashen nicht


Die Voraussetzungen für den Desktop-Rechner sind:

  • Mindestens 250 GB Plattenplatz
  • Mindestens 16 GB RAM

Es gibt im Netz viele gute fertige Android-Images. Zum Beispiel: LineageOS.
Es gibt auch Anleitungen zum Bauen eines Android GSI aus den Quellen mit Hilfe einer Entwicklungsumgebung auf dem Desktop-Rechner, vorzugsweise Linux.
Zum Beispiel hier: How-to-build-a-GSI

Das Bauen kann viele Stunden dauern. In dem Prozess wird die gesamte Power des Rechners gebraucht und in der Zeit ist der Rechner kaum produktiv für andere Zwecke. Ich empfehle: Das Ganze sollte auf einem eigenen Rechner laufen.

Ich habe mir das Projekt von AndyCGYan angesehen, eine Lineage-Entwickler. Auf seiner Webseite befinden sie die Build-Scripte und die aktuellen Sicherheits-Patches.
Der letzte Befehl startet den Built-Prozess (der mehrere Stunden dauern kann).

bash lineage_build_unified/buildbot_unified.sh treble 32B A64B 64B

Dieses Script „buildbot_unified.sh“ habe ich verändert. Es stoppt und gibt folgende Möglichkeiten:

  • Installieren eines eigenen Boot-Images
  • Löschen von prebuilt Apps (von Lineage vorinstalliert)
  • Vorinstallieren von Apps aus F-Droid Repo.

Hier ein Beispiel für eine Maske, die beim Booten hinterleuchtet wird.

#!/bin/bash
echo ""
echo "LineageOS 18.x Unified Buildbot"
echo "ATTENTION: this script syncs repo on each run"
echo "Executing in 2 seconds - CTRL-C to exit"
echo ""
sleep 2

cd ~/lineage-18.x-build-gsi/


if [ $# -lt 2 ]
then
    echo "Not enough arguments - exiting"
    echo ""
    exit 1
fi

MODE=${1}
if [ ${MODE} != "device" ] && [ ${MODE} != "treble" ]
then
    echo "Invalid mode - exiting"
    echo ""
    exit 1
fi

PERSONAL=false
if [ ${!#} == "personal" ]
then
    PERSONAL=true
fi

# Abort early on error
set -eE
trap '(\
echo;\
echo \!\!\! An error happened during script execution;\
echo \!\!\! Please check console output for bad sync,;\
echo \!\!\! failed patch application, etc.;\
echo\
)' ERR

START=`date +%s`
BUILD_DATE="$(date +%Y%m%d)"
WITHOUT_CHECK_API=true
WITH_SU=true

echo "Preparing local manifests"
echo -e "Press 'y' and 'enter' to create manifest or
if the manifest is current, then skip this point with 'enter' only."
read
if [ "$REPLY" == "y" ]; then
	mkdir -p .repo/local_manifests
	cp ./lineage_build_unified/local_manifests_${MODE}/*.xml .repo/local_manifests
fi

cd ~/lineage-18.x-build-gsi
echo ""

echo "Syncing repos"
echo -e "Press 'y' and 'enter' to syncing repo or
if the repo is current, then skip this point with 'enter' only."
read
if [ "$REPLY" == "y" ]; then
	repo sync -c --force-sync --no-clone-bundle --no-tags -j$(nproc --all)
	echo ""
fi

echo "Setting up build environment"
echo -e "Press 'y' and 'enter' for settings or
if the settings is current, then skip this point with 'enter' only."
read
if [ "$REPLY" == "y" ]; then
	source build/envsetup.sh &> /dev/null
	mkdir -p ~/build-output > /dev/null
fi

cd ~/lineage-18.x-build-gsi
echo ""

apply_patches() {
    echo "Applying patch group ${1}"
    bash ~/treble_experimentations/apply-patches.sh ./lineage_patches_unified/${1}
}

prep_device() {
    :
}

prep_treble() {
    apply_patches patches_treble_prerequisite
    apply_patches patches_treble_phh
}

finalize_device() {
    :
}

finalize_treble() {
    rm -f device/*/sepolicy/common/private/genfs_contexts
    cd device/phh/treble
    git clean -fdx
    bash generate.sh lineage
    cd ../../..
}

build_device() {
    brunch ${1}
    mv $OUT/lineage-*.zip ~/build-output/lineage-18.1-$BUILD_DATE-UNOFFICIAL-${1}$($PERSONAL && echo "-personal" || echo "").zip
}

build_treble() {
    case "${1}" in
        ("32B") TARGET=treble_arm_bvS;;
        ("A64B") TARGET=treble_a64_bvS;;
        ("64B") TARGET=treble_arm64_bvS;;
        (*) echo "Invalid target - exiting"; exit 1;;
    esac
    lunch lineage_${TARGET}-userdebug
    make installclean
    make -j$(nproc --all) systemimage
    make vndk-test-sepolicy
    mv $OUT/system.img ~/build-output/lineage-18.1-$BUILD_DATE-UNOFFICIAL-${TARGET}$(${PERSONAL} && echo "-personal" || echo "").img
}

echo "Applying patches"
echo -e "Press 'y' and 'enter' for patching or
if patches is current, then skip this point with 'enter' only."
read
if [ "$REPLY" == "y" ]; then
	prep_${MODE}
	apply_patches patches_platform
	apply_patches patches_${MODE}
	if ${PERSONAL}
	then
		echo "personal"
		apply_patches patches_platform_personal
		apply_patches patches_${MODE}_personal
	fi
	finalize_${MODE}
fi

cd ~/lineage-18.x-build-gsi
echo ""

echo -e "Replace own bootimage? 

---------------------------
Here is the possibility to change the boat image to your own wishes.
Go to   ~/lineage-18.x-build-gsi/frameworks/base/core/res/assets/images/android-logo-mask.png  
and open the image to edit with gimp ... 
... and have fun!
---------------------------

When you have changed the image, press 'y' and 'enter'.

if you do not want to do anything, then only 'enter'."

read
if [ "$REPLY" == "y" ]; then
	rm -Rf ~/lineage-18.x-build-gsi/vendor/lineage/bootanimation > /dev/null
fi

# delete prebuid apps
echo -e "Delete linageOS prebuild Apps. 
Then press 'y' and 'enter'.
also skip this point with 'enter' only."
read
if [ "$REPLY" == "y" ]; then
	cd ~/lineage-18.x-build-gsi/packages/apps
	[ -e Camera2 ] && rm -R Camera2
	[ -e Contacts ] && rm -R Contacts
	[ -e DeskClock ] && rm -R DeskClock
	[ -e Gallery2 ] && rm -R Gallery2
	[ -e Messaging ] && rm -R Messaging
	[ -e AudioFX ] && rm -R AudioFX
	[ -e UniversalMediaPlayer ] && rm -R UniversalMediaPlayer
	[ -e HTMLViewer ] && rm -R HTMLViewer
	[ -e Eleven ] && rm -R Eleven
	[ -e xactCalculator ] && rm -R xactCalculator
	[ -e FlipFlap ] && rm -R FlipFlap
	[ -e FMRadio ] && rm -R FMRadio
	[ -e Jelly ] && rm -R Jelly
	[ -e Recorder ] && rm -R Recorder
	
	# Switch off tests
	cd ~/lineage-18.x-build-gsi/platform_testing/build/tasks/tests/
	for i in platform_test_list.mk; do
		fgrep -v -e "CalendarTests" -e "ContactsTests" -e "camera_client_test" "$i" > tmp.mk;
	done
	mv tmp.mk platform_test_list.mk
fi

cd ~/lineage-18.x-build-gsi
echo ""

# install new apps
echo -e "Install new prebuild Apps from f-droid. 
Then press 'y' and 'enter'.
then skip this point with 'enter' only."
read
if [ "$REPLY" == "y" ]; then
	if [ ! -e prebuilts/myapps ]; then mkdir prebuilts/myapps; fi
	cd prebuilts/myapps
	rm -Rf bin &> /dev/null
	
	repo="https://f-droid.org/repo/"

	addCopy() {
		addition=""
		if [ "$2"  == org.mozilla.fennec_fdroid ];then
			unzip bin/$1 lib/*
			addition="
LOCAL_PREBUILT_JNI_LIBS := \\
$(unzip -lv bin/$1 |grep -v Stored |sed -nE 's;.*(lib/arm64-v8a/.*);\t\1 \\;p')

			"
		fi
		if [ "$2" == com.google.android.gms ] || [ "$2" == com.android.vending ] ;then
			addition="LOCAL_PRIVILEGED_MODULE := true"
		fi
		cat >> Android.mk <<EOF
include \$(CLEAR_VARS)
LOCAL_MODULE := $2
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := bin/$1
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_OVERRIDES_PACKAGES := $3
$addition
include \$(BUILD_PREBUILT)

EOF
		echo -e "\t$2 \\" >> apps.mk
	}

	rm -Rf apps.mk lib
	cat > Android.mk <<EOF
LOCAL_PATH := \$(my-dir)

EOF
	echo -e 'PRODUCT_PACKAGES += \\' > apps.mk

	mkdir -p bin

	#downloadFromFdroid packageName overrides
	downloadFromFdroid() {
		mkdir -p tmp
		[ "$oldRepo" != "$repo" ] && rm -f tmp/index.xml
		oldRepo="$repo"
		if [ ! -f tmp/index.xml ];then
			#TODO: Check security keys
			wget --connect-timeout=10 $repo/index.jar -O tmp/index.jar
			unzip -p tmp/index.jar index.xml > tmp/index.xml
		fi
		marketvercode="$(xmlstarlet sel -t -m '//application[id="'"$1"'"]' -v ./marketvercode tmp/index.xml || true)"
		apk="$(xmlstarlet sel -t -m '//application[id="'"$1"'"]/package[versioncode="'"$marketvercode"'"]' -v ./apkname tmp/index.xml || xmlstarlet sel -t -m '//application[id="'"$1"'"]/package[1]' -v ./apkname tmp/index.xml)"
		if [ ! -f bin/$apk ];then
			while ! wget --connect-timeout=10 $repo/$apk -O bin/$apk;do sleep 1;done
		fi
		addCopy $apk $1 "$2"
	}
	cat > list.txt << EOF
me.phh.superuser
org.fdroid.fdroid "F-Droid"
org.fdroid.fdroid.privileged "F-Droid-Privileged"
net.sourceforge.opencamera "Open Camera"
com.simplemobiletools.gallery.pro "simple Gallery"
com.menny.android.anysoftkeyboard "anysoftkey"
com.simplemobiletools.contacts.pro "simple Contacts"
at.bitfire.davdroid "davdroid"
nekox.messenger "telegram"
net.kollnig.missioncontrol.fdroid "tracker control"  
org.smssecure.smssecure "SMS"
eu.faircode.email "Fairmail"
net.sourceforge.solitaire_cg "Solitaire"
de.christinecoenen.code.zapp "Zapp Mediathek"
de.schildbach.oeffi "Bus und Bahn"
de.rwth_aachen.phyphox "Physik"
net.osmand.plus "Landkarte, Navi"
org.schabi.newpipe "UTube"
com.foobnix.pro.pdf.reader "Bookreader"
com.github.catfriend1.syncthingandroid "syncthing"
de.rki.covpass.app "covid pass"
de.rki.covpass.checkapp "covid check"
com.machiav3lli.backup "backup"
org.gateshipone.odyssey "Music player"
de.baumann.browser "FOSS Browser"
de.danoeh.antennapod "Podcast"
EOF
	while read -r line; do
		downloadFromFdroid $line
	done < list.txt
	echo >> apps.mk	
	
cat > .gitignore << EOF
bin/
apps.mk
Android.mk
EOF
	rm -Rf tmp &> /dev/null
	rm -f list.txt

	echo "fine"
fi

cd ~/lineage-18.x-build-gsi
echo ""

echo echo "Starting build"
echo -e "Now start the build prozess. 
Press 'y' and 'enter'.
Or skip this point with 'enter' only."
read
if [ "$REPLY" == "y" ]; then
	for var in "${@:2}"
	do
		if [ ${var} == "personal" ]
		then
			continue
		fi
		echo "Starting $(${PERSONAL} && echo "personal " || echo "")build for ${MODE} ${var}"
		build_${MODE} ${var}
	done
	ls ~/build-output | grep 'lineage' || true
fi

END=`date +%s`
ELAPSEDM=$(($(($END-$START))/60))
ELAPSEDS=$(($(($END-$START))-$ELAPSEDM*60))
echo "Buildbot completed in $ELAPSEDM minutes and $ELAPSEDS seconds"
echo ""

Sorry für mein bäd änglisch. 😉