2022. november 30., szerda

Gyorskeresés

Android ROM fejlesztési napló

Írta: | Kulcsszavak: Android . ROM . Pixel Experience . Xiaomi . MI 9T . DavinciCodeOS

[ ÚJ BEJEGYZÉS ]

Korábban kifejtettem a véleményem az Android jelenlegi állapotáról ([link]), ami azóta sem változott sokat. Az a típus vagyok, aki szereti megérteni az eszközei működését, azonban Android fronton meglehetősen ellustultam, ugyanis azt a tendenciát vettem észre, hogy ahogy egyre több felhasználót vonz be az Android, a Google egyre több kaput zár be és próbálja a népet terelgetni egy adott felhasználási mód felé. Mára már az egyedi romok nagy része is csak az alap rendszer, illetve néhány apró kiegészítés, ami többnyire kozmetikai. Igazán nagy fejlesztések azonban főként hivatalosan az Android frissítésekkel jönnek.

Ezért döntöttem úgy, hogy átlagos userként fogom én is használni a rendszert és a legutóbbi telefonomat - ami egy Xiaomi Mi 9T - nem is rootoltam az első két évében, illetve hagytam a gyári MIUI-t futni a maga összes bugjával. Igen ám, de az évek során egészen megszerettem a telefont hardver szempontból, és ha váltani kéne, a mai napig nem tudnám eldönteni, hogy mire váltanék. A maga kiugró szelfikamerája egészen különleges megoldás és sajnálom, hogy ma már nem igen csinálnak hasonlót, marad az értesítési sáv nagy részét értelmetlenné tevő notch és társai... A SoC is elég erős napi használatra, illetve sokáig bírja az akku és van jack.

Bár a készülék lassan négy éves lesz, ezek mellett nem bírom elengedni, így inkább megtanultam a miui összes nyűgjével együtt élni. Azonban az utóbbi évben használhatatlanul lassú lett, a Xiaomi elengedte a telefont security patchek és frissítések tekintetében és magára maradt az amúgy még mindig tökéletesen korszerű hardver. Ekkor döntöttem úgy, hogy legyalulom a rendszert és egyedi rom fog futni rajta megfelelő támogatással. Ekkor találtam rá a Pixel Experience romra, ami minimalista stílusával nagyon megtetszett, illetve szeretem a Pixel funkciókat, így nem volt kérdés, hogy ezt teszem fel. Sajnos a hivatalos verzió ebből a romból a készülékre elég balul sikerült, kb 5 óra alatt lemerült a telefon az amúgy 2 napos üzemidő helyett, az ujjlenyomat olvasó jó ha 4 érintésre feloldotta a képernyőzárat és tele volt laggal a rendszer. Ekkor találtam rá a Marco03 féle nem hivatalos Pixel Experience buildre, amit a Picsel Inexperience névre keresztelt el és direkt a telefonra készült ez a verzió megannyi optimalizációval, extrával. Hihetetlen stabil lett tőle a felhasználói élmény és ki tudtam próbálni az Android 11-et és a 12-t is jóval a MIUI-s megjelenése előtt.

Azonban Marco-nak közben úgy alakult, hogy nem igazán tudott foglalkozni az elmúlt hónapokban a frissítésekkel, közben pedig kijött egy igen fontos security patch, illetve az Android 12L névre hallgató első alverziója az Android 12-nek. Ezen a ponton döntöttem úgy, hogy vége a kényelmes felhasználói életnek és már csak a kíváncsiságból is megpróbálok összehozni egy friss romot a Marco féle források, saját változtatások és a legfrissebb Pixel Experience alapján. Ezzel kicsit az is volt a célom, hogy megtapasztaljam, mennyit változott a folyamat majdnem egy évtized óta, amikor utoljára romolgattam.

Az írás pedig azért születik, mert a folyamat során már most belefutottam egy pár érdekességbe, amiről mindenképp szerettem volna írni. De akkor csapjunk is bele!

Első körben érdekelt, hogy miféle "előképzettség" szükséges manapság a romolgatáshoz, mert az volt a tapasztalat, hogy nem igazán vannak konkrét instrukciók a neten, amik pedig vannak, azok igen régiek. Ezek alapján nem igazán tűnt megfoghatónak a dolog. Ami talán széleskörűen elterjedt vélemény, hogy mindenhez is érteni kell egy picit, értsd.: linux minden mennyiségben, otthon kell lenni Javában és C++ban, alapvetően az Android lelkivilágának és a különböző hardverek ismerete is szükséges, illetve magának a cél hardvernek az ismerete, amire a jövőbeni port készül. Nos ez mind igaz, de arra kellett rájönnöm, hogy romot önmagában buildelni már létező forrásokból nem egy nagy etwas és apróbb módosításokat sem bonyolult eszközölni. Ami kell az sok türelem, kitartás és egy erős gép / szerver. :D

Az egész írás a Pixel Experience (továbbiakban PE) rom köré van kihegyezve, de pontosan ez a folyamat lejátszható más rendszerek esetében. Induljunk ki abból, hogy leszedjük a forrásokat a gépre, amelyet PE esetén a következő manifest repó tartalmaz: [link] Mivel az Android forrása hatalmas, egyszerűbb volt komponensekre bontani és így külön-külön lehet ezeket változtatni/frissíteni. Ez a manifest repó tartalmazza gyakorlatilag az összes ilyen komponens listáját és gondoskodik arról később, hogy minden le legyen töltve a fordító gépre amire csak szükség lehet.

Lehúzva a forrásokat a gépemre arra jutottam, hogy ehhez bizony az itthoni gépem édeskevés lesz, ugyanis a forrás nagyjából 220 GB helyet emészt fel lebuildelve, így szempont az, hogy az embernek jó internete legyen, korszerű és sokmagos processzora, illetve az se árt, ha van bőven RAM és nem a háttértár kerül folyamatos írásra/olvasásra. Az SSD pedig alapvető, ha azt akarjuk, hogy emberi időkben mérve leforduljon, mivel több millió forrásfájlt kell megnyitnia a fordítónak.

Körbenéztem, hogy melyik hosztingnál tudnék bérelni sokmagos VPS-t, ahol adnak dedikált magokat (normál VPS hosztoknál ugyanis nem szeretik, ha egyvalaki felemészti az összes erőforrást; a dedikált szerver meg teljesen felesleges, mert egyszer van használva pár órára a szerver és utána csak állna üresen) és arra jutottam, hogy a Vultr, Linode, Amazon, Hetzner, Digital Ocean és a Google Cloud jöhet szóba (egyébként ha bárki tud még órára számlázó VPS providert, ne kímélje a komment szekciót). Egyelőre a Hetzner tűnik befutónak, ugyanis a magyar áfával 0.72 euróból per óra adnak 48 magos 192 GB RAM-os konfigot, ami tökéletesnek bizonyult.

A terv az lett tehát, hogy először megtapasztalom, hogyan lehet összehozni a buildet, aztán csinálok egy egyszerűen konfigurálható build környezetet, kölcsönzök egy erős VPS-t, lefordítom a dolgokat, majd megszabadulok a VPS-től és így jó eséllyel kijövök 2 euró alatt per build.

Most, hogy ezt lefixáltuk, jöhet a build disztró kérdése. Ha az ember készít egy docker containert később, akkor teljesen mindegy, mi fut a hoszton (csak az a fontos, hogy glibc alapú legyen, így az alpine pl felejtős), de a szárnypróbálgatások idejére nekem az Ubuntut ajánlották, mert tartalmazza az összes függőséget. Nos, Ubuntut én elvből nem telepítek, illetve egészségesebbnek tűnt egy olyan hosztról fordítani, ahol frissek a csomagok. Sokszor egy friss kernel csodákra képes. Így maradtam a Fedoránál. Természetesen ettől el lehet térni, de arra számítani kell, hogy ahány disztró, annyi különböző függőség és ezek összevadászása időigényes.

A cikk írása pillanatában ezekkel a csomagokkal lefordult a rendszer:

sudo dnf install -y git bison ncurses-compat-libs ccache perl patchutils automake autoconf binutils flex gcc gcc-c++ gdb glibc-devel libtool pkgconf pkgconf-m4 pkgconf-pkg-config strace bzip2 python3 make openssl openssl-devel curl procps-ng openssh-clients freetype freetype-devel rsync xz

Következő lépésként pedig győződjünk meg róla, hogy nem root felhasználóként fordítunk a továbbiakban. Egyáltalán nem lesz rá szükség és erősen ellenjavallott. Ha ez adott, akkor szerezzük be a repo parancsot a google tárhelyről, ami majd letölti a forrásokat:

mkdir -p ~/.local/bin && curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/.local/bin/repo && chmod +x ~/.local/bin/repo

Majd készítsünk egy mappát a forrásoknak:

mkdir ~/pe
cd ~/pe

Ezt követően állítsuk be a git felhasználónkat globálisan, ugyanis érdekes módon ezek megadása nélkül néhány forrás eleve le sem töltődik:

git config --global user.name MrDini
git config --global user.email mrdini@mrdini.me

Nyilván saját adatokkal tessék kitölteni.

Ha megvan, végre inicializálhatjuk a repókat (itt a -b twelve adja meg, hogy az Android 12 forrásokat szeretnénk):

repo init -u https://github.com/PixelExperience/manifest -b twelve

Majd lehúzhatjuk a forrásokat:

export THREAD_COUNT=$(nproc --all)
repo sync -c --jobs-network=$(( $THREAD_COUNT < 16 ? $THREAD_COUNT : 16 )) -j$THREAD_COUNT --jobs-checkout=$THREAD_COUNT --force-sync --no-clone-bundle --no-tags
unset THREAD_COUNT

Ez a pár parancs elsőre túlzásnak érződhet paraméterezés szempontjából, azonban úgy vettem észre, hogyha sokmagos gépe van az embernek, akkor simán előfordulhat, hogy több szálon töltené le a forrásokat, mint ami a Googlenek szimpatikus és ezért mindig belefut az ember a 429 too many requests hibába és nem tudja lehúzni a forrásokat. Ez a max 16 mag abszolút hasra ütött szám, úgy tűnik ez még rendben van. Természetesen a többi folyamatra (pl. kicsomagolás) már az összes mag használva van. A többi flag pedig azt állítja be, hogy feleslegesen ne kerüljön le a teljes változtatás lista, nekem elég a legfrissebb forrás a buildhez. Egyébként a későbbiekben ezt a parancsot kell csak ismételten kiadni, ha frissíteni szeretnénk a már letöltött forrásokat. Amit már egyszer leszedett, azt nem fogja még egyszer letölteni, csak ha van változás. Viszont ha van helyi változás, azt gyalulni fogja.

Meg is vagyunk az alapokkal és megvan minden, ami az alap rendszer fordításához szükséges lenne. Igen ám, de vannak eszköz specifikus repók is, hiszen az Androidnak meg kell mondani, hogy az adott eszköz milyen tulajdonságokkal, illetve egyéb egyedi komponensekkel rendelkezik. Ha az eszköz támogatva lenne hivatalosan a PE csapat által, akkor erre eleve a repo parancsok kiadása után lehetőség van, de a Mi 9T már nem támogatott hivatalosan a projekt által, illetve amúgy sem jött be a gyári konfig. Marad a tákolás.

Ahhoz, hogy saját forrásokból tudjunk dolgozni, nyilván szükség lesz a forrásokra. A cikk feltételezi, hogy már áll rendelkezésre forrás az eszközhöz és azt nem a nulláról kell felépíteni. Szerencsére én is hasonló helyzetben voltam, hiszem már léteztek más AOSP alapú rendszerek a telefonhoz, amelyeket csak forkolni kellett. MI 9T esetében nagyjából az összes rom ezeken a forrásokon alapszik: [link]

Node rá kellett jönni, hogy pontosan mik ezek a repók, mennyi is kell belőle pontosan és hogy hogyan illesztem be a build könyvtárba az összes eszköz specifikus mappát is. Szerencsére ez egészen jól támogatott a Google soong buildere által így mindössze annyit kell csinálni, hogy létrehoz az ember egy local_manifests mappát:

mkdir .repo/local_manifests

Majd létrehoz benne egy local_manifest.xml fájlt az eszköz dolgaival:

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<!-- Remotes -->
<remote name="gh" fetch="https://github.com/" />

<!-- Add new repositories -->
<project path="device/xiaomi/davinci" name="DavinciCodeOS/device_xiaomi_davinci" remote="gh" revision="12" />
<project path="device/xiaomi/sm6150-common" name="DavinciCodeOS/android_device_xiaomi_sm6150-common" remote="gh" revision="12" />
<project path="kernel/xiaomi/sm6150" name="DavinciCodeOS/msm-4.14" remote="gh" revision="12.1" />
<project path="vendor/xiaomi" name="DavinciCodeOS/android_vendor_xiaomi" remote="gh" revision="12" />
<project path="hardware/xiaomi" name="DavinciCodeOS/android_hardware_xiaomi" remote="gh" revision="lineage-19.1" />
</manifest>

A fájl igazából önmagáért beszél, az első sorban megadom neki a git repó elérési útját, majd a projekteknél ezt a remote-t hivatkozom meg. A projekteknél a name nyilván a github.com/ utáni elérési út lesz, míg a path a helyi elérési utat fogja jelenteni a forrás gyökerétől számítva. A revision pedig az adott git repó branchét jelöli meg.

Ezek után ha nyom az ember ismét egy repo syncet, akkor szépen letölti ezeket a forrásokat is.

Most, hogy lent van az összes kellő forrás, tisztázzuk, hogy az én értelmezésem szerint melyik repó mire jó pontosan.

Az első projekt a device/xiaomi/davinci helyre kerül és esetemben innen kerül letöltésre. Nyilván ez jelenti a telefon lelkét, definiálja, hogy milyen paraméterei vannak a készüléknek, hogy a build system tudja, milyen különleges hardver elemekkel van felszerelve az adott eszköz. Ez minden eszköz buildhez szükséges és ha ebbe akarunk belenyúlni, akkor érdemes jól áttanulmányozni más, hasonló készülékek forrásait, ugyanis nem sok doksit találtam a Google oldalán erre vonatkozóan.

Második repónk a csatasorban a device/xiaomi/sm6150-common névre hallgat és nem hiszem, hogy az eszközök többsége rendelkezik ilyen "common" repóval. Ez is a device mappa alé kerül, tehát nem lesz meglepő, hogy ugyanúgy a device tree-hez lesz köze. Azonban ő, az előző függőségeként lesz jelen. Ilyet akkor szoktak csinálni, ha nagyon hasonló hardvert raknak több telefonba is, de például a kamera modul működése eltér, vagy a képernyőfelbontás stb. Ilyenkor az összes közös tulajdonság megy a common repóba és az adott eszköz specifikus dolgai meg az előző projektben kapnak helyet. Ezeket a forrásokat pedig nem a build system, hanem az előző projekt fogja értelemszerűen beolvasni és feldolgozni.

No a harmadik a kernel/xiaomi/sm6150 nevet kapta. Gyanúsan, ha a kernel mappába kerül, akkor ő lesz az eszköz specifikus kernel. Ezen nincs nagyon mit magyarázni, ha valakinek volt már dolga linux kernel forgatással, annak nagy előnye van. Itt is ugyanúgy van defconfig és Android 12-től kezdve alapértelmezetten clanggal van fordítva. :C A MI 9T tulajok abban a különlegesen szerencsés helyzetben vannak, hogy használhatnak egy InfinityKernel névre hallgató projektet, amit egy lengyel srác fejleszt és a különlegessége, hogy az összes telefon számára felesleges sallang ki lett gyomlálva belőle, illetve sok-sok optimalizációt kapott és frissebb kernelekből visszaportolt forrásokat kapott, így marha jól teljesít.

Következő vizsgálati alanyunk a vendor/xiaomi. No ez a repó tartalmazza az összes általában zárt forrású, gyártótól származó fájlokat. Nehéz összefoglalni, hogy miket tartalmaz pontosan, ugyanis egyes eszközökön még a gyártótól származó hangfájlokat is, de ami a lényegét adja szerintem mégis, hogy az itt található libek segítségével képes az Android API kommunikálni a mindenféle hardveres komponenssel. Ezek általában mind "proprietary" jelölést kapnak, azaz ezekkel vigyázni kell licencelés szempontjából, mivel ténylegesen a gyártók tulajdonát képezik. Ide még annyit érdemes megjegyezni, hogy okkal nem vendor/xiaomi/davinci-t adtam meg elérési útnak, mivel ez is egy egyesített vendor repó. Így fog a vendor/xiaomi/davinci mappa is a helyére kerülni.

Végül pedig van a hardware/xiaomi repó, ami a MI 9T esetében szintén hardveres komponensek kezelésére tartalmaz kódokat, de szerencsére ezek nyílt forrású komponensek. Ezek nyilván hasonló könyvtárakká fognak fordulni, mint a vendor libek. Vele még sok tapasztalatom nincs, egyedül a powermanagert említem meg, mint amit többször sikerült már piszkálni és mint igen fontos szereplő.

Most, hogy ezeket mind rendbe raktuk és le van töltve a teljes forrás, ideje a fordítást előkészíteni. A következő ugyan elég Pixel Experience és MI 9T specifikus lépés, azonban elengedhetetlen, ugyanis ha ez a commit nincs visszavonva, akkor remekül lefordul a build, de nem lesz se kameránk, se mobilhálózatunk, se wifink, stb. Az történik ugyanis, hogy megváltoztatták a vbmeta ellenőrzés módját, ami miatt MI 9T-n nem fog betöltődni és emiatt tapasztaljuk, hogy az egész build használhatatlan lesz. Vonjuk vissza:

cd system/core
git revert ec10d3cf6e328da90dd4a388761d2d26543fce8f
cd ../../

Egy másik patch is elengedhetetlen a forrásokra, ez viszonylag újkeletű. Szerencsére ha ezt nem alkalmazzuk, már a build során is errort kapunk, így nem kell keresgélni, hogy éppen melyik változás miatt nem megy a telefon. Emögött a történet az, hogy nemrég kapott a vendor és a device tree a MI 9T esetében egy kis refaktort. Szépen át lett írva, hogy a régi Android.mk-s makefile helyett egy korszerű Android.bp definiálja az összes vendor fájlt. Igen ám, de a régi makefile esetében nem volt probléma, ha felüldefiniáltunk a forrásban egy azonos nevű libraryt, itt azonban probléma lesz, ugyanis a .bp fájlokat a google féle android builder, a soong olvassa be. A bp fájl lényegében egy json szerű leíró, amit beolvas minden könyvtárbból, majd ha összerak egy egyesített makefilet az eredményből, a build elején. Igen ám, de itt nem lehet 2 db ugyanolyan nevű lib. Mégpedig a libmegface például valami oknál fogva a Pixel Experience romban az external/faceunlock mappában is definiálva van. Megnéztem, hogy melyiket érdemes eldobni és arra jutottam, hogy bár a Pixel Experience féle lib kétségtelenül frissebb, viszont nem lehet tudni, hogy melyik eszközből szedték ki, illetve ha ezt próbálom meghagyni, meghal a kamera rendszerszinten. Marad a xiaomi féle. Ehhez pedig csináltam egy patchet:

cd external/faceunlock
wget https://github.com/DavinciCodeOS/Cathedra/raw/main/patches/0011-faceunlock-remove-conflicting-broken-dependencies.patch
git am -3 0011-faceunlock-remove-conflicting-broken-dependencies.patch
cd ../../

Nagyjából ennyivel már tényleg kész is vagyunk és következhet a build lépések kifundálása. Első körben érdemes ccache-t állítani, ami nyilván nem kötelező, de nagyon fel tudja gyorsítani a fordítást. Azonban nekem ez sem ment olyan simán, alapból a user home könyvtárában szeretne egy rejtett mappát használni. Viszont erre nekem egy csak olvasható hibát dobott a build és nem fordult le, mégha ténylegesen az összes joga megvolt oda írni. Erre egy ocsmány workaroundnak átraktam a ccache mappát a /tmp alá, így ráadásul ramdiszkbe kerül és jó gyorsan fog belőle olvasni/írni:

export USE_CCACHE=1
mkdir -p /tmp/ccache
export CCACHE_DIR="/tmp/ccache"
ccache -M 100G

Ja és itt láthatóan 100 GB helyett kapott a cache. Erre vigyázni kell, hogy tényleg legyen annyi. Ha kevés ramos gépen buildel az ember, célszerűbb SSD-re létrehozni ugyanezt a mappát. Illetve kisebb cache mérettel is szépen működik a dolog, azonban minél többet adsz neki, annál gyorsabb lesz.

A buildhez először adjuk ki a következő parancsot, hogy sikerüljön bekonfigurálni az összes fordításhoz szükséges környezeti változót:

source build/envsetup.sh

Majd használjuk a lunch parancsot, ami megmondja a build rendszernek, hogy éppen aktuálisan melyik eszköz romját akarjuk fordítani és ezek alapján beállít még pár eszközhöz kapcsolódó változót. Ha eddig jól dolgoztunk és csak annyit adunk ki, hogy "lunch", akkor meg kell jelennie egy aosp_davinci-user, -userdebug és -eng opcióknak. Innen az eng build tartalmazza a legtöbb debuggoláshoz szükséges funkciót, a user build az, amit a Google javaslata alapján a userek kezébe célszerű adni, mint végleges termék és van a userdebug build, aminek az a célja, hogy kevesebb debug funkcióval rendelkezzen, mint az eng build, minél jobban hasonlítson a user buildre (ezért a név, userdebug), de mégis pl rootolható legyen, hogy lehessen debuggolni. Én pl battery drain debugra használtam, mert az eng build nem volt realisztikus a marha sok debug miatt, a user build meg nem volt túl bőbeszédű. Sok romot egyébként userdebug módban adnak ki, mert macerás user-t buildelni és mára már nincs sok különbség a két variáns közt méretben sem.

Mi most egy userdebug buildet szeretnénk, így adjuk ki.

lunch aosp_davinci-userdebug

Végül pedig fordítsuk le a teljes rendszert:

make -j$(nproc --all) bacon

Ez bizony beletelhet jó időkbe. Nálam a 48 magos VPS-en 47 perc alatt fordul le a nulláról a mutatvány. Szerencsére, ha már egyszer lefordult, akkor nyilván nem kezd mindent a nulláról, így akinek otthon erős gépe van és azon fordít, annak nem kell mindig ilyen sokat várnia, de aki VPS-t bérel, az igencsak sokat vár...

Ha minden jól megy, akkor az out/target/product/davinci/ mappa alatt fog várni a már flashelhető állapotú frissen fordult rom. Érdemes egy teljes backupot csinálni flashelés előtt, lementeni az IMEI számokat és a widevine certet például, aztán mehet a használat. :) Ha pedig a build errorra fut, akkor lehet kezdeni debuggolni, hogy mi lehet a gond pontosan. Általában az errorok elég beszédesek.

Végszónak pedig megjegyezném, hogy nyilván nem célszerű ezeket a parancsokat mindig egyesével végigzongorázni, ugyanis ha kimarad egy-egy lépés, utána nem kellemes mindig a nulláról kezdeni a dolgokat. Ha érdekel valakit, akkor itt megtalálja az én kezdetleges build szkriptem: [link] Nagyjából ugyanaz az alapja, mint a cikknek, csak itt már vannak extra funkciókat hozzáadó patchek is, nem csak egy nyers PE build.

Hozzászólások

(#1) Oldman2


Oldman2
veterán

Köszi a leírást!
:R

További hozzászólások megtekintése...
Copyright © 2000-2022 PROHARDVER Informatikai Kft.