Android: ezt neked Google!

Boldogan gondolok vissza Androidos fénykoromra, mikor a zsebemből nem egy telefont, hanem egy apró méretű számítógépet húztam elő. El kell ismernem, a Google és a CyanogenMod team egy olyan élményt alkotott számomra az Android 4.2.2 alapú CM10.1 ROM-mal, amit ténylegesen élmény volt használni. Ezek után pedig következett a hanyatlás, ahogy az lenni szokott. Valamiért néhány fejes úgy gondolta, hogy jobb elvenni a már létezőt, mint adni. Azóta főverzióról főverzióra zárják el a fejlesztőktől a lehetőségeket, biztonsági kockázat címszóra hivatkozva. Értem én, saját biztonságom meg stb, de minek vettem telefont lassan egy számítógép áráért, ha a biztonságom érdekében zárják el fokozatosan előlem és a fejlesztők elől pontosan azokat a dolgokat, amelyek miatt többet költöttem telefonra.

Először jöttek a kisebb merényletek talán, hogy mobilnetet nem lehet kapcsolgatni csak úgy. Mindenképpen root szükséges, ha azt bármelyik applikációból szeretnénk mondjuk ütemezve megtenni. Nem sokkal később, a Kitkat megjelenésével nagyot lépett a Google és ráébredt, hogy mekkora biztonsági kockázat rejlik már abban, ha az alkalmazások csak úgy tudnak írni/olvasni az SD kártyára/-ról minden további nélkül. Így a KitKaten (4.4) egészen egyszerűen nem lehet mezei alkalmazásként (aka nem rendszerapp) akárhova írni, csak az adott alkalmazás saját mappájába. Hát hogy ez hogy jött nekik, meg hogy merték meglépni, azt a mai napig nem értem, de úgy tűnik sokan mások sem. Mivel a közfelháborodás hatására a következő verziók már legalább adnak lehetőséget az írásra amennyiben a felhasználó egy tallózó activity-n engedélyt ad a hozzáféréshez. Azt leszámítva, hogy ezzel is elvágták a visszafele kompatibilitást, még azt mondom egészen tűrhető az elképzelés, hogy bízzuk a felhasználóra a döntést. Bár ezt a funkciót activity-vel nem rendelkező appoknál elég macerás alkalmazni. A megvalósítás meg merem kockáztatni, hogy az utolsó pillanat műve volt... :U Volt viszont egy értelmesnek mondható lépésük is, az emulált tárhely bevezetése. Az alap ötlet emögött, hogy a telefont úgy lehessen számítógéphez csatlakoztatni, hogy közben a telefon is változatlanul lássa a csatlakoztatott partíció(ka)t, az ide telepített applikációk és adatok továbbra is elérhetőek legyenek. Így viszont dobni kellett a korábbi USB Mass Storage kapcsolódási módot, lett helyette az MTP. Rengeteg hátránya közt talán a kompatibilitás kérdése tűnik ki, jó ha az asztali gép felismeri, de médialejátszóknál pl. nem jellemző a támogatás. Illetve az, hogy egyszerre csupán egy fájlműveletet engedélyez. De a wiki-n fel van tüntetve a többi limitációja is. És akkor ennek tetejébe jött az aduász, a SELinux bevezetése. Ok, tényleg tök jó, hogy a kernel majd eldönti mihez ad kinek és hogyan hozzáférést, de a telefonomon nehogy már egy policy csomag határozza meg, hogy mit lehet és mit nem. Az OS adjon csak egy alapból tiszta környezetet, döntse el a user, hogy mit telepít és legyen az ő felelőssége az óvatosság. Egy generáció már lassan felnőtt az Androidon, csak működne. Vagy legyen a kettő kombinálva. Cégeknél RHEL alatt eleget kínlódtam SELinux-szal, ott meg is értem, hiszen enterprise szféra. De egy telefonon minek. :F

Ez a sok korlátozás eredményezte azt, hogy a 4.2 alatt tökéletesen működő CIFS csatolásom, minek segítségével a teljes NAS-om tartalmát élvezhettem, mint egy helyi mappa, ment a levesbe. Annak idején @Somatom linkelte nekem a CIFS Managert, ami ezt lehetővé tette. Tényleg imádtam ezt a megoldást, mert a PowerAMP-tól elkezdve az MX Playeren át az összes médiám lekezelte.

És ennyit a régészkedésről. Mára az egész pompából csupán annyi maradt, hogy ha szükségem van valamilyen dokumentumra, videóra, bármire, akkor a Total Commander SFTP pluginjével kénytelen vagyok megnyitás előtt letölteni a teljes fájlt az SD kártyára, ami hely és időigényes. Más nincs, mert Google szerint nem biztonságos.

Rengeteg álmatlan éjszakán mentem keresztül, hogy kitaláljam hogy kerüljem meg a korlátozást valahogy, aztán hagytam a dolgot. Évekkel később találtam rá az rclone-ra (írtam róla korábban is), ami korlátlan tárhelyével teljesen kiváltotta a NAS szerepét. Viszont havonta plusz 4 dollárom ráment arra, hogy mobilokon is elérjem a fájljaim rendesen, mert bár minden eszközöm, a NAS-ok, a gép, a Raspberry Pi és még sorolhatnám, támogatja, de az Android nem. Pedig technikailag minden adott. Linux kernelt használ (ok, jó pár egyedi patch-csel) és a FUSE is engedélyezve van a kernelben, mióta a Google bevezette az emulált tárhelyet. Eltérés ugyan van a desktopom, ami GNU/Linux alapú disztrót futtat és az eszköz közt, amely csak Busybox-szal igyekszik hasonló programokat pótolni, de ez minket az rclone futtatása kapcsán nem is érdekel. Viszont a libc eltérés annál inkább, ugyanis a Google Bionic motorja (ez lett az Androidba ágyazva) eltér a PC-s glibc-től, hiányzik pár hasznos hívás (részletek itt). Ez annyit jelent, hogy a portolás során mindenképp a Google féle fordítót érdemes használni.

Ezeket mérlegelve láttam némi fényt abban, hogy megint legyenek felcsatolható megosztásaim, hát neki is álltam. Az alany egy Galaxy J1 (2016) volt. Igen, a modell, amit a Telekom annak idején ingyen osztogatott az előfizetései mellé. A J3, J5 kis tesója és nem egy atomgép a SoC, de pont elfér a zsebben, teszi a dolgát rendesen és nem fáj, ha esetleg holnap már tégla lesz a házam falán. Viszont nem volt valami népszerű telefon a maga korában sem, így egyetlen főzött ROM, se TWRP, se root zip nem készült hozzá. Szerencsére rootolni már régen sikerült (systemless, így a SELinuxnak se zavar be - külön írás lehetne az is :DDD), illetve a TWRP is elég könnyen lefordult, ezekbe most nem is mennék bele.

Tehát adott volt egy J1 (2016) teljesen gyári ROM-mal, gyári kernellel, roottal + busybox-szal. Gyorsan letöltöttem innen a legfrissebb ARM 32 bit verziót, kicsomagoltam a telefonra, majd kerestem egy üres mappát, ahol kontárkodhatok a /data alatt (a /system/bin alá másolva a telefon a SELinux miatt örökös bootloopba keveredett). Találtam egy üres /data/tmp mappát, így az rclone ide lett másolva. Belső tárhelyről, SD kártyáról nem lehet futtatni, ezek noexec-cel vannak felcsatolva. Root alól sikerült is futtatni:

root@j1xlte:/ # cd /data/tmp
root@j1xlte:/data/tmp # ./rclone --config /sdcard/rclone.conf config
2019/07/16 23:23:37 NOTICE: Config file "/sdcard/rclone.conf" not found - using defaults
No remotes found - make a new one
n) New remote
s) Set configuration password
q) Quit config
n/s/q> q
root@j1xlte:/data/tmp #

Hála az égnek, legalább az rclone-t nem kell majd magamnak lefordítani. :C Egyszerűbb lesz a frissítés a jövőben. Viszont a --config kézi megadása minden parancsnál rendkívül fontos, mivel egyébként rclone koma a /rclone.conf-ba ment - ha létezik. Ha nem, akkor meg létrehozza. Az meg instant bootloop a SELinux miatt. A belső tárhely, ami nekem /sdcard alá lett csatolva, viszont tökéletes hely a számára. Szuper, már legalább fájlokat tudok kézzel le-, feltölteni a tárhelyeimről, de ez még mindig messze van a csatolástól. Ugyanis ahhoz szükség lesz valamire, ami a fájlrendszerembe "virtuálisan" beleilleszti az rclone-os tárhelyeim tartalmát, így élvezve helyileg a NAS-om én a Google Drive-om fájljait. Itt jön kapóra a Google-nek köszönhetően szinte kötelező kernel résszé vált fuse modul használata. Közvetlenül vele sajnos az rclone nem képes kommunikálni, de a magyar fejlesztésű libfuse pont erre a célra lett megálmodva. Az rclone-nak csak a csomagban található fusermount binárisra van szüksége, így őt kellett beszereznem valahonnan. Gondoltam, ha a Google eddig ennyi mindent elvett tőlem, adjon is vissza valamit. Hátha valaki fordított már fusermount-ot Androidra. Találtam is nagy sokára egy XDA posztot, ahol valaki közzétett egy tökéletesnek látszó ARM portot. Fel is ment az eszközre, majd futtatáskor ez fogadott:

WARNING: linker: fusermount: unused DT entry: type 0x6ffffffe arg 0xd08
WARNING: linker: fusermount: unused DT entry: type 0x6fffffff arg 0x1
CANNOT LINK EXECUTABLE: cannot locate symbol "__register_atfork" referenced by "fusermount"...

Belenézve a Bionic forrásba hanar rájöttem, hogy ez a __register_atfork valóban hiányzik az én libc-mből és majd csak a 23-as API szinttől (Android 6.0 [link]) tudna ilyet a telefon.

Tovább kutakodva belefutottam a CyanogenMod FUSE portjába, ami direkt Androidra készült.

https://github.com/LineageOS/android_external_fuse

Sőt, találtam egy cm-12.1-re hallgató forrást is, ami pontosan az 5.1.1-re készült, remekül futna a kis J1-en. [link]

Egyetlen gondja van csupán, hogy nem tudom lefordítani. Nincs Androidra dedikált (NDK-s) telepítő a projektben. Így ötletem sincs hogy fordították ezt anno telefonokra, de pl direktben a telefonról lefordulna a kód ebben az állapotban is. Nekem viszont PC-n kellett volna produkálnom egy Android kompatibilis verziót. Így jutottam el odáig, hogy kézzel összeraktam egy működő forrást. Akit érdekel, szeretné lefordítani magának, feltettem ide:

https://github.com/MrDini123/Fusermount4Android

Instrukciók a README-ben. De itt van az összes, az NDK-m által támogatott platformra elkészült fusermount bináris: [link]

Ez is ment a /data/tmp-be, futtattam és ki is írta a helpjét mindenféle panaszkodás nélkül! :Y Szuper, akkor most gyorsan próbáljuk is ki rclone alól, hogy meg tudja-e hívni. Ehhez az kell, hogy az rclone lássa, tehát legyen a PATH változóban az elérési út. Nekem a /data/tmp alá került, tehát:

export PATH=$PATH:/data/tmp

Aztán próbaképp megeresztettem egy rclone mount parancsot, hátha csatolja. No, még mit nem:

Fatal error: failed to mount FUSE fs: fusermount: fork/exec /data/tmp/fusermount: permission denied

Sebaj, csak a szokásos - gondoltam én:

chmod 755 /data/tmp/fusermount

Bár furcsáltam, hogy én magam shellből, rootként tudtam futtatni kézzel a binárist, de az rclone már nem tudja. Hát és nem is jött be, a hiba ugyanaz volt. :O OK, ez SELinux lesz, innen is üdvözlöm! Gondoltam nem probléma, rutinból lelövöm az egészet, nem kell az nekem, csak akadályoz:

setenforce 0

Majd a getenforce parancsra azt kellett volna írnia, hogy Permissive, de nem. Makacsul írta továbbra is az Enforcing feliratot. Mostmár elkértem a kernel forrást a Samsungtól is a telefonhoz, hátha okosabb leszek. Meg is lett a jelenség okozója:

DCONFIG_ALWAYS_ENFORCE=true

Ez az apró beállítás a vanilla kernelbe fordítva. Tehát akármit csinálok, a SELinux visszakapcsol. Hacsak nem csinálok egyedi kermelt a Samsungos alapján, de arra nem vitt még a lélek azóta sem. Nem tudom, hogy ez a Google műve, vagy a Samsungé, de top ötlet, tényleg hálás vagyok érte. :W

Mindenesetre futott mind az rclone, mind a fusermount magában remekül. Egyedül egymást nem tudták meghívni. Eszembe jutott, hogy valami trükk csak lehet, mert a /system/bin alatt is rengeteg gyári bináris van, azok meg gond nélkül csinálnak child folyamatokat. Viszont azt az opciót ki kellett lőnöm, hogy rendberakom minden reboot után a /data/tmp-t úgy, hogy a SELinux engedje a futtatást, akár a /system/bin esetében. De ekkor eszembe jutott a systemless root, amit korábban csináltam. :) Pontosan ezt csinálja, a /su/bin mappán engedélyezi a futtatást. Így oda bemásoltam mindent és láss csodát, elindult! Rcloneostul, mindenestül.

Akkor itt vége is? Áh, még mit nem. Túl egyszerű lenne...Fel akartam csatolni a /storage/extSdCard/drive alá a Drive megosztásom, ami rclone oldalról hiba nélkül meg is történt, viszont a kedvenc fájlkezelőmben (Total Commander perpill) már csak egy üres, 0 byte fájlként jelent meg a mappának keresztelt megosztás. Rclone debug logban feltűnt, hogy a rendszer folyamatosan valamilyen lekezeletlen műveletet szeretne végrehajtani rajta, de ez kb másodpercenként 30x megtörténik. Gyanakodni kezdtem, hogy mitől lehet ez, majd egy ls -la kimenet a külső SD-men megmondta a tutit. Bármilyen fájlt, vagy mappát helyezek rá, s nem számít, hogy ezt milyen appból, de még az sem, ha root shellből teszem, a tulajdonos megváltozik root-ra, a csoport pedig "sdcard_r"-re. Bingó, mivel a csatolást eleve root alól hoztam létre, csak a csoportot kell rendbe rakni.

Szerencsére az rclone-nak van egy --gid kapcsolója is, pont erre a célra [link]. Értelemszerűen a csoport azonosítója kell neki. Hát, nekem a neve megvolt, csak az azonosító hiányzott. Ennek a kiderítésére egy elég spontán megoldást találtam, de sikerült.

Elsőként a systemui-ra gyanakodtam, bizonyára lesz köze az SD jogokhoz. Tehát megkerestem a folyamat azonosítóját:

ps | grep systemui

Eredmény:

u0_a38 3065 2157 1152188 83444 ffffffff b6dbd690 S com.android.systemui

Nekem a vastagon kiemelt 3065-ös PID (Process ID) kellett. Szintén remek, nézzünk is rá a folyamat részleteire zanzásítva:

root@j1xlte:/ # cat /proc/3065/status
Name: ndroid.systemui
State: S (sleeping)
Tgid: 3065
Pid: 3065
PPid: 2157
TracerPid: 0
Uid: 10038 10038 10038 10038
Gid: 10038 10038 10038 10038
FDSize: 256
Groups: 1015 1028 1035 3001 3002 3006 9997 50038
VmPeak: 1160828 kB
VmSize: 1152348 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 102248 kB
VmRSS: 82848 kB
VmData: 200060 kB
VmStk: 8192 kB
VmExe: 44 kB
VmLib: 92172 kB
VmPTE: 420 kB
VmSwap: 54696 kB
Threads: 46
SigQ: 0/7122
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000009204
SigIgn: 0000000000000000
SigCgt: 00000002000094f8
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000000000000000
Seccomp: 0
Cpus_allowed: f
Cpus_allowed_list: 0-3
voluntary_ctxt_switches: 254459
nonvoluntary_ctxt_switches: 211290

A Groups: 1015 sorra lettem figyelmes. Elővettem az Android forrást, s meg is leltem, amire kíváncsi voltam (igazából ezzel kellett volna kezdenem, de így, hogy tudtam pár csoport ID-t, már könnyebb volt meglelni). Íme a kérdéses definíció [link]:

#define AID_SDCARD_RW 1015 /* external storage write access */

Eléggé gyanús, hogy jó lesz ez nekem, ha az SD-re szándékozok csatolni. Így tehát a végleges parancs valami ilyesmi lett:

nohup rclone mount dinidrive: /storage/extSdCard/drive --gid 1015 --allow-other --vfs-cache-mode minimal &

A gid flaget túltárgyaltuk, csoportot állít. Az allow-other magától értetődő, nem csak a root, hanem minden app hozzá fog férni a megosztáshoz, az utolsó flag pedig nem kötelező, de ajánlott. Fájl feltöltéskor nem egyből megy majd a felhőbe az adat, hanem az rclone lokálisan tárolja le, majd tölti fel a háttérben azt. Hasznos, ha ingadozik a net minősége, illetve így mehet fel egyszere több fájl is. Kevés tárhely mellett viszont nem ajánlott. A parancs végén az & shellből ismerős lehet, a háttérbe küldi a processzt, a nohup pedig segít, hogy a terminál megszakítása után (kilépés mondjuk a terminál appból) is menjen a csatolás mint egy daemon a háttérben.

Kilőni pedig a fusermount -u /storage/extSdCard/drive paranccsal lehet, ha éppen nem használja semmilyen app.

Több részlet az rcloneról itt.

És igen, ezek után visszakaptam a sokáig hiányolt eszméletlen gyors és zéró adatvesztés lehetőségű netes tárhelyem. Vicces látni, hogy van egy mappám, ami 1 PB hellyel rendelkezik az SD-n. :D

Egyébként, ha valaki kedvet kapna rclone-t tenni a telefonjára és a SELinux eleve permissive-re van állítva, esetleg lehet állítani, bátran lehet másolni a binárisokat a /system/bin alá is. Aki pedig Magisk systemless rootot használ, érdemes ránézni erre a projektre: [link]. Nem próbáltam, de ígéretesnek tűnik.

Az Android kapcsán meg egyre jobban azt érzem, mint amit az Apple telefonoknál. Kap az ember egy remek hardvert, de szoftveresen semmi szabadsága nincs, egy homokozóba van szorítva minden alkalmazás és kb csak internetet kap. Semmi egyedit nem lehet így kihozni magából a telefonból, csak a már legyártott Lego kockákból építkezhetünk.

A Kúgli helyében meg nem aggódnék ilyen szinten. Eleve ott bukik meg a dolog, hogy nem követelnek standardot, minden gyártó azt módosít az Androidon és helyez a készülékére, amit csak szeretne. Ezen kéne módosítani először. A másik meg, hogy amíg nem képes az ébresztő appom öntudatra ébreszteni a telefont, hogy az szándékosan törjön az életemre, nem sok értelmét látom ennyire erőltetni. Nem rossz, ha figyel, de döntse el a user, hogy mi legyen a rendszer válasza egy-egy kiszűrt eseményre. Annyi kompromisszum létezne...

Még van hozzászólás! Tovább