Z racji tego, że utrzymuję maksymalny porządek, czystość/jakość kodu i implementację najnowszych technologii (takich jak np. aroma) postanowiłem się podzielić użytecznymi linkami dla "samonauki" na bazie ArchiDroida.
Jeśli ktoś nie jest w temacie to ArchiDroid to jeden z najbardziej skomplikowanych ROMów pod kwestią "wnętrzności", czyli tego co nie jest widoczne dla przeciętnego użytkownika.
Wszystko co znajdziecie w tym temacie bazuje na rozwiązaniach SGS3 I9300, aczkolwiek duża część z nich jest ogromnie uniwersalna dzięki mojemu "podejściu" do kwestii budowy custom romów, dzięki temu przeportowanie AD na inne urządzenie oparte o armv7a to kwestia 20-30 minut. Uważam to za spory sukces (Przykład: MrturDroid na Nexusa 4)
Ale do rzeczy...
Temat został stworzony dla osób, które chciałyby zacząć przygodę w tworzeniu swoich własnych romów.
Będę go uzupełniał w miarę wolnego czasu. Pierw pojawią się bardziej zaawansowane porady, a te najbardziej podstawowe zostawię sobie na koniec, jako że większa część osób je zna i może się nimi podzielić. Idea tematu jest dość prosta - naszym developerom też się coś należy, a moja skrzynka PM staje się pełna w ciągu tygodnia 😉.
Skarbnica wiedzy do własnoręcznej nauki:
Mój GitHub
Mój profil na XDA
ArchiDroid
ArchiDroid na XDA
98ArchiDroid_RunOnce
99ArchiDroid_Init
Rzeczy, które trzeba już mieć w małym palcu - https://forum.android.com.pl/topic/300314-kompendium-wiedzy-odin-bootloader-recovery-backup-rom-root-sudden-death/ . W szczególności słowniczek pojęć i rozeznanie w temacie (na bazie SGS3).
Słowniczek nowych pojęć, które musisz znać:
Bash - Język skryptowy, a konkretniej powłoka systemowa. Android domyślnie korzysta z powłoki sh - shell, ale bash sam w sobie jest tylko rozszerzeniem shella, więc skrypty zachowują dużą kompatybilność. Skrypty w bashu to najprostsza i najskuteczniejsza metoda zaawansowanego przygotowywania systemu operacyjnego (romu).
init.d - Folder ze skryptami wykonywanymi podczas bootowania. Skrypty są wykonywane przez kernel jako user root i napisane w Bash'u.
#1 Permissions
Jak dobrze wiemy odpowiednie accessy to coś co chcemy mieć. Temat accessów przewija się co chwila w zasadzie bo nie ma jednej i uniwersalnej metody na ustawienie całego Androida.
Również spotkałem się z tym problemem i już jakiś czas temu w moim ArchiDroidzie zastosowałem podwójny system permissions. Na czym polega? Instalator Aroma ustawia odpowiednie dla większości accessy, które bez problemu sobie działają, a skrypt post-instalacyjny permfix załatwia resztę.
Tutaj nieco więcej o tym, prosto z changeloga ArchiDroida 1.4.1:
1.4.1
- [EXPERIMENTAL] Fixed very important permission issues.
# It now works a bit different. I've cleaned up permissions and made them as universal as I could.
# There are 2 stages now - aroma stage and android stage.
# Aroma stage is same as before - permissions are set during the installation.
# Android stage is something new, implemented by me to ensure that everything is allright.
# After an initital sleep of 2 minutes (to make sure system is fully loaded) on the first boot my new script 98archidroid_permfix will be called from /system/etc/init.d.
# It basicly checks if everything is allright with proper permissions and if not - it fixes permissions so they're always up to date and fixed.
# After clean finish it automatically delete itself to prevent being called on the next boot.
# This is a very professional way to deal with a permissions, we're calling it directly from loaded system so we're sure that system is fully loaded and permission are finally set in the initial stage.
# Probably I've just fixed a million of problems which may or may not arrive in the future. We're now sure that permissions are ok after installation.
# I highly recommend to NOT reboot or shut down system before first 4-5 minutes of running on the first boot.
# Even if something like that happen my script should be automatically executed on the second boot and fix broken permissions made by first instance anyway.
# But I didn't test it for such thing and I can't assure you that everything will work as it should.
# That shouldn't be a problem anyway because my script only modifies already broken permissions (not everyone) so it could only be better, not worse.
# As for now I left debug log in /sdcard/fix_permissions.log. If you're interested in that feel free to browse around after these 5 minutes of work.
# I'm going to remove this log in the next release after being sure that it works like a charm.
No i oczywiście sam kod.
/system/etc/init.d/zzzArchiDroid_PermFix
#!/system/bin/sh
# ArchiDroid Universal Fix Permissions Script
# JustArchi@JustArchi.net
# Coming from https://github.com/CyanogenMod/android_bootable_recovery/blob/jellybean/utilities/fix_permissions
# Not Disabled
#exit 0
# ArchiDroid Semaphore
# Wait until we see some android processes to consider boot is more or less complete (credits to AndiP71)
# NOTICE: We've already finished runonce and init scripts so it shouldn't be needed anyway but let's make sure
while ! pgrep com.android ; do
sleep 1
done
# Now that is checked, let's just wait another tiny little bit
sleep 10
VERSION="2.04"
# Defaults
DEBUG=0 # Debug off by default
LOGGING=1 # Logging on by default
VERBOSE=1 # Verbose on by default
# Messages
UID_MSG="Changing user ownership for:"
GID_MSG="Changing group ownership for:"
PERM_MSG="Changing permissions for:"
# Programs needed
ECHO="busybox echo"
GREP="busybox grep"
EGREP="busybox egrep"
CAT="busybox cat"
CHOWN="busybox chown"
CHMOD="busybox chmod"
MOUNT="busybox mount"
UMOUNT="busybox umount"
CUT="busybox cut"
FIND="busybox find"
LS="busybox ls"
TR="busybox tr"
TEE="busybox tee"
TEST="busybox test"
SED="busybox sed"
RM="busybox rm"
WC="busybox wc"
EXPR="busybox expr"
DATE="busybox date"
# Initialise vars
CODEPATH=""
UID=""
GID=""
PACKAGE=""
REMOVE=0
NOSYSTEM=0
ONLY_ONE=""
SIMULATE=0
SYSREMOUNT=0
SYSMOUNT=0
DATAMOUNT=0
SYSSDMOUNT=0
FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
FP_STARTEPOCH=$( $DATE +%s )
if $TEST "$SD_EXT_DIRECTORY" = ""; then
#check for mount point, /system/sd included in tests for backward compatibility
for MP in /sd-ext /system/sd;do
if $TEST -d $MP; then
SD_EXT_DIRECTORY=$MP
break
fi
done
fi
fp_usage()
{
$ECHO "Usage $0 [OPTIONS] [APK_PATH]"
$ECHO " -d turn on debug"
$ECHO " -f fix only package APK_PATH"
$ECHO " -l disable logging for this run (faster)"
$ECHO " -r remove stale data directories"
$ECHO " of uninstalled packages while fixing permissions"
$ECHO " -s simulate only"
$ECHO " -u check only non-system directories"
$ECHO " -v disable verbosity for this run (less output)"
$ECHO " -V print version"
$ECHO " -h this help"
}
fp_parseargs()
{
# Parse options
while $TEST $# -ne 0; do
case "$1" in
-d)
DEBUG=1
;;
-f)
if $TEST $# -lt 2; then
$ECHO "$0: missing argument for option $1"
exit 1
else
if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then
ONLY_ONE=$2
shift;
else
$ECHO "$0: missing argument for option $1"
exit 1
fi
fi
;;
-r)
REMOVE=1
;;
-s)
SIMULATE=1
;;
-l)
if $TEST $LOGGING -eq 0; then
LOGGING=1
else
LOGGING=0
fi
;;
-v)
if $TEST $VERBOSE -eq 0; then
VERBOSE=1
else
VERBOSE=0
fi
;;
-u)
NOSYSTEM=1
;;
-V)
$ECHO "$0 $VERSION"
exit 0
;;
-h)
fp_usage
exit 0
;;
-*)
$ECHO "$0: unknown option $1"
$ECHO
fp_usage
exit 1
;;
esac
shift;
done
}
fp_print()
{
MSG=$@
if $TEST $LOGGING -eq 1; then
$ECHO $MSG | $TEE -a $LOG_FILE
else
$ECHO $MSG
fi
}
fp_start()
{
if $TEST $SIMULATE -eq 0 ; then
if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then
DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 )
if $TEST $DEBUG -eq 1; then
fp_print "/system mounted on $DEVICE"
fi
if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then
$MOUNT -o remount,rw $DEVICE /system
SYSREMOUNT=1
fi
else
$MOUNT /system > /dev/null 2>&1
SYSMOUNT=1
fi
if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then
$MOUNT /data > /dev/null 2>&1
DATAMOUNT=1
fi
if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " $SD_EXT_DIRECTORY " "/proc/mounts" ) -eq 0; then
$MOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
SYSSDMOUNT=1
fi
fi
if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then
LOG_FILE="/data/archidroid_fix_permissions.log"
else
LOG_FILE="/sdcard/archidroid_fix_permissions.log"
fi
if $TEST ! -e "$LOG_FILE"; then
> $LOG_FILE
fi
fp_print "$0 $VERSION started at $FP_STARTTIME"
}
fp_chown_uid()
{
FP_OLDUID=$1
FP_UID=$2
FP_FILE=$3
#if user ownership doesn't equal then change them
if $TEST "$FP_OLDUID" != "$FP_UID"; then
if $TEST $VERBOSE -ne 0; then
fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'"
fi
if $TEST $SIMULATE -eq 0; then
$CHOWN $FP_UID "$FP_FILE"
fi
fi
}
fp_chown_gid()
{
FP_OLDGID=$1
FP_GID=$2
FP_FILE=$3
#if group ownership doesn't equal then change them
if $TEST "$FP_OLDGID" != "$FP_GID"; then
if $TEST $VERBOSE -ne 0; then
fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'"
fi
if $TEST $SIMULATE -eq 0; then
$CHOWN :$FP_GID "$FP_FILE"
fi
fi
}
fp_chmod()
{
FP_OLDPER=$1
FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 )
FP_PERSTR=$2
FP_PERNUM=$3
FP_FILE=$4
#if the permissions are not equal
if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then
if $TEST $VERBOSE -ne 0; then
fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)"
fi
#change the permissions
if $TEST $SIMULATE -eq 0; then
$CHMOD $FP_PERNUM "$FP_FILE"
fi
fi
}
fp_all()
{
FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $WC -l )
I=0
$CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | while read all_line; do
I=$( $EXPR $I + 1 )
fp_package "$all_line" $I $FP_NUMS
done
}
fp_single()
{
FP_SFOUND=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE | wc -l )
if $TEST $FP_SFOUND -gt 1; then
fp_print "Cannot perform single operation on $FP_SFOUND matched package(s)."
elif $TEST $FP_SFOUND = "" -o $FP_SFOUND -eq 0; then
fp_print "Could not find the package you specified in the packages.xml file."
else
FP_SPKG=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE )
fp_package "${FP_SPKG}" 1 1
fi
}
fp_package()
{
pkgline=$1
curnum=$2
endnum=$3
CODEPATH=$( $ECHO $pkgline | $SED 's%.* codePath="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
PACKAGE=$( $ECHO $pkgline | $SED 's%.* name="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
UID=$( $ECHO $pkgline | $SED 's%.*serId="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
GID=$UID
APPDIR=$( $ECHO $CODEPATH | $SED 's%^\(.*\)/.*%\1%' )
APK=$( $ECHO $CODEPATH | $SED 's%^.*/\(.*\..*\)$%\1%' )
#debug
if $TEST $DEBUG -eq 1; then
fp_print "CODEPATH: $CODEPATH APPDIR: $APPDIR APK:$APK UID/GID:$UID:$GID"
fi
#check for existence of apk
if $TEST -e $CODEPATH; then
fp_print "Processing ($curnum of $endnum): $PACKAGE..."
#lets get existing permissions of CODEPATH
OLD_UGD=$( $LS -ln "$CODEPATH" )
OLD_PER=$( $ECHO $OLD_UGD | $CUT -d ' ' -f1 )
OLD_UID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f3 )
OLD_GID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f4 )
#apk source dirs
if $TEST "$APPDIR" = "/system/app"; then
#skip system apps if set
if $TEST "$NOSYSTEM" = "1"; then
fp_print "***SKIPPING SYSTEM APP ($PACKAGE)!"
return
fi
fp_chown_uid $OLD_UID 0 "$CODEPATH"
fp_chown_gid $OLD_GID 0 "$CODEPATH"
fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
elif $TEST "$APPDIR" = "/data/app" || $TEST "$APPDIR" = "/sd-ext/app"; then
fp_chown_uid $OLD_UID 1000 "$CODEPATH"
fp_chown_gid $OLD_GID 1000 "$CODEPATH"
fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
elif $TEST "$APPDIR" = "/data/app-private" || $TEST "$APPDIR" = "/sd-ext/app-private"; then
fp_chown_uid $OLD_UID 1000 "$CODEPATH"
fp_chown_gid $OLD_GID $GID "$CODEPATH"
fp_chmod $OLD_PER "rw-r-----" 640 "$CODEPATH"
fi
else
fp_print "$CODEPATH does not exist ($curnum of $endnum). Reinstall..."
if $TEST $REMOVE -eq 1; then
if $TEST -d /data/data/$PACKAGE ; then
fp_print "Removing stale dir /data/data/$PACKAGE"
if $TEST $SIMULATE -eq 0 ; then
$RM -R /data/data/$PACKAGE
fi
fi
fi
fi
#the data/data for the package
if $TEST -d "/data/data/$PACKAGE"; then
#find all directories in /data/data/$PACKAGE
$FIND /data/data/$PACKAGE -type d -exec $LS -ldn {} \; | while read dataline; do
#get existing permissions of that directory
OLD_PER=$( $ECHO $dataline | $CUT -d ' ' -f1 )
OLD_UID=$( $ECHO $dataline | $CUT -d ' ' -f3 )
OLD_GID=$( $ECHO $dataline | $CUT -d ' ' -f4 )
FILEDIR=$( $ECHO $dataline | $CUT -d ' ' -f9 )
FOURDIR=$( $ECHO $FILEDIR | $CUT -d '/' -f5 )
#set defaults for iteration
ISLIB=0
REVPERM=755
REVPSTR="rwxr-xr-x"
REVUID=$UID
REVGID=$GID
if $TEST "$FOURDIR" = ""; then
#package directory, perms:755 owner:$UID:$GID
fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
elif $TEST "$FOURDIR" = "lib"; then
#lib directory, perms:755 owner:1000:1000
#lib files, perms:755 owner:1000:1000
ISLIB=1
REVPERM=755
REVPSTR="rwxr-xr-x"
REVUID=1000
REVGID=1000
fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
elif $TEST "$FOURDIR" = "shared_prefs"; then
#shared_prefs directories, perms:771 owner:$UID:$GID
#shared_prefs files, perms:660 owner:$UID:$GID
REVPERM=660
REVPSTR="rw-rw----"
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
elif $TEST "$FOURDIR" = "databases"; then
#databases directories, perms:771 owner:$UID:$GID
#databases files, perms:660 owner:$UID:$GID
REVPERM=660
REVPSTR="rw-rw----"
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
elif $TEST "$FOURDIR" = "cache"; then
#cache directories, perms:771 owner:$UID:$GID
#cache files, perms:600 owner:$UID:GID
REVPERM=600
REVPSTR="rw-------"
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
else
#other directories, perms:771 owner:$UID:$GID
REVPERM=771
REVPSTR="rwxrwx--x"
fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
fi
#change ownership of directories matched
if $TEST "$ISLIB" = "1"; then
fp_chown_uid $OLD_UID 1000 "$FILEDIR"
fp_chown_gid $OLD_GID 1000 "$FILEDIR"
else
fp_chown_uid $OLD_UID $UID "$FILEDIR"
fp_chown_gid $OLD_GID $GID "$FILEDIR"
fi
#if any files exist in directory with improper permissions reset them
$FIND $FILEDIR -type f -maxdepth 1 ! -perm $REVPERM -exec $LS -ln {} \; | while read subline; do
OLD_PER=$( $ECHO $subline | $CUT -d ' ' -f1 )
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
fp_chmod $OLD_PER $REVPSTR $REVPERM "$SUBFILE"
done
#if any files exist in directory with improper user reset them
$FIND $FILEDIR -type f -maxdepth 1 ! -user $REVUID -exec $LS -ln {} \; | while read subline; do
OLD_UID=$( $ECHO $subline | $CUT -d ' ' -f3 )
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
fp_chown_uid $OLD_UID $REVUID "$SUBFILE"
done
#if any files exist in directory with improper group reset them
$FIND $FILEDIR -type f -maxdepth 1 ! -group $REVGID -exec $LS -ln {} \; | while read subline; do
OLD_GID=$( $ECHO $subline | $CUT -d ' ' -f4 )
SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
fp_chown_gid $OLD_GID $REVGID "$SUBFILE"
done
done
fi
}
date_diff()
{
if $TEST $# -ne 2; then
FP_DDM="E"
FP_DDS="E"
return
fi
FP_DDD=$( $EXPR $2 - $1 )
FP_DDM=$( $EXPR $FP_DDD / 60 )
FP_DDS=$( $EXPR $FP_DDD % 60 )
}
fp_end()
{
if $TEST $SYSREMOUNT -eq 1; then
$MOUNT -o remount,ro $DEVICE /system > /dev/null 2>&1
fi
if $TEST $SYSSDMOUNT -eq 1; then
$UMOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
fi
if $TEST $SYSMOUNT -eq 1; then
$UMOUNT /system > /dev/null 2>&1
fi
if $TEST $DATAMOUNT -eq 1; then
$UMOUNT /data > /dev/null 2>&1
fi
FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
FP_ENDEPOCH=$( $DATE +%s )
date_diff $FP_STARTEPOCH $FP_ENDEPOCH
fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)"
}
#MAIN SCRIPT
fp_parseargs $@
fp_start
if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then
fp_single "$ONLY_ONE"
else
fp_all
fi
# We've finished so we're no longer needed
sysrw
rm -f $0
sysro
# End
fp_end
exit 0
Dzięki temu jesteśmy pewni, że skrypt odpali się jako ostatni, wyczeka ew. załadowania systemu, ustawi odpowiednie accessy i się sam usunie.
UWAGA! Metoda jest czysto archiwalna, ponieważ NIE POWINNO jej się stosować przede wszystkim na Androidach ze wsparciem SELinux oraz na ROMach, w który "wiemy" co się dzieje. Nie polecam stosowania jej na dłuższą metę.
#2 Flashowanie /data/data
Ostatnio natknąłem się na potrzebę sflashowania programu razem z jego "ustawieniami". Ustawienia danej aplikacji siedzą w /data/data/codenameaplikacji/shared_prefs. O ile na Stockach jest to dość proste (jeszcze) bo wystarczy wyłącznie dane wrzucić w odpowiednie miejsca podczas flashowania to z Cyanogenem i generalnie z Androidem 4.2.2 będą już problemy.
1. Jeśli wrzucimy potrzebne pliki podczas flashowania to Android podczas pierwszego bootu nam je usunie
2. Jeśli wrzucimy potrzebne pliki podczas flashowania i zablokujemy je poprzez chattr +i to aplikacje owszem będą działały, ale Android wywali nam błąd o niekompatybilności danych i niestabilności systemu, a tego nie chcemy.
3. Jeśli wrzucimy dane w skrypcie post-instalacyjnym (po załadowaniu) to dane będą dopiero załadowane od następnego rebootu.
Pytanie brzmi. Jak sobie z tym poradzić?
Nie powiem, że miałem dość ładną zagadkę przez ostatni tydzień, ale w końcu sobie poradziłem z jej rozwiązaniem. Stworzyłem skrypt init.d, który w pełni spełnia moje oczekiwania.
Skrypt jest mojego własnego autorstwa, pisany od zera. Doceń moją pracę jeśli będziesz chciał go użyć 🙂.
/system/etc/init.d/98ArchiDroid_RunOnce
#!/system/bin/sh
# ArchiDroid FirstBoot Script
# JustArchi@JustArchi.net
# Not Disabled
#exit 0
# ArchiDroid Semaphore
# Wait until we see some android processes to consider boot is more or less complete (credits to AndiP71)
while ! pgrep com.android ; do
# Now let's MAKE SURE that our settings are in fact applied, only if we don't have shared_prefs already (prevent non-clean override)
if [ -d /data/media/0/archidroid/firstboot ]; then
cd /data/media/0/archidroid/firstboot
for FOLDER in * ; do
if [ -d /data/data/$FOLDER ]; then
if [ ! -d /data/data/$FOLDER/shared_prefs ]; then
mv $FOLDER/* /data/data/$FOLDER
# Let's set basic permissions because ArchiDroid_Permfix will fix it anyway really soon
ADOWNER=`stat -c %U /data/data/$FOLDER`
chown -hR $ADOWNER.$ADOWNER /data/data/$FOLDER
chmod -R 755 /data/data/$FOLDER
# And we're done!
rm -Rf $FOLDER
ADOWNER=""
fi
fi
done
fi
# Sleeping time
sleep 2
done
# Now that is checked, let's just wait another tiny little bit
sleep 10
# If we have any more runonce scripts then it's the right place for that
# I'm runonce script so let's clean everything and delete myself
# NOTICE
# ArchiDroid has sysro/sysrw support, change that to remount system rw if needed
rm -Rf /data/media/0/archidroid/firstboot
sysrw
rm -f $0
sysro
# Finish
exit 0
Zasada działania jest bardzo prosta. Podczas flashowania tworzymy przykładowa strukturę /data/media/0/archidroid/firstboot/, a następnie w tym folderze firstboot umieszczamy foldery zgodne z nazwami folderów aplikacji, do których chcemy wrzucić dane. Czyli przykładowo dla boeffli będzie to /data/media/0/archidroid/firstboot/com.near.boefflasound/shared_prefs. Mój skrypt RunOnce wyczeka aż Android wyczyści stare dane i utworzy foldery, a następnie od razu wrzuci do nich dane naszych aplikacji i ustawi odpowiednie accessy. Efekt jest taki, że Androidowi nie będzie to przeszkadzać, a mój permfix (który również dołączam w punkcie #1) ładnie przeczyści pozostałości.
Skryptowi oczywiście nadajemy flagę (chmod) +x, żeby był wykonany podczas bootowania (i umieszczamy go w /system/etc/init.d).
UWAGA! Mój system wykrywania załadowanego systemu wstrzymuje jakiekolwiek dalsze skrypty przed uruchomieniem tzn. uruchomią się, ale dopiero po całkowitym zakończeniu wykonywania się runonce'a, dlatego upewnijmy się, że jest to ostatni lub jeden z ostatnich skryptów do wykonania (poprzez odpowiednią alfabetyczną nazwę). Ew. możemy zastosować mój mały "hack" z backgroundem, który wprowadziłem w nowszych wersjach RunOnce. W tym punkcie przedstawiam ogólną "ideę" działania skryptu, a nie jego najnowszą i najlepszą wersję.
UWAGA! Jeśli Twój rom nie ma wsparcia dla sysro/sysrw zamień te komendy na mount -o remount,ro /system i rw.
#? GitHub / (CR)LF / Linux vs. Windows
Nie od dziś wiadomo, że Windows i Linux się nie lubią, również nawet pod taką błahą kwestią jak znaki nowej linii.
Co to jest znak nowej linii? To specjalny znak, który określa przejście do nowego wiersza w plikach tekstowych (i nie tylko). W linuxie jest to LF, w windowsie CRLF, po więcej informacji zapraszam na google.
No i dobra, fajnie, ale o co tu chodzi? Na pewno nie raz spotkałeś się z sytuacją w której dany plik tekstowy X był otwierany przez notatnik jako jeden wielki tekst ciągły, w ogóle bez "enterów", podczas gdy Notepad++ otwierał go normalnie. Czemu tak? Ano właśnie ten plik pochodzi z Linuxa lub systemu, który nie jest zgodny z Windowsem i używa innego zakończenia plików. Notepad++ jest na tyle sprytny, że sam wykrywa które zakończenie jest używane i właśnie tego używa, niestety nie każdy tak się zachowuje.
W gicie jest taki fajny zapisik konfiguracyjny o nazwie core.autocrlf. Może on przyjmować różne wartości, ale nas interesują dwie - true oraz false. Na wszystkich linuxach i gitach linuxowych ta funkcja przyjmuje wartość false, podczas gdy na gicie windowsowym true. Ponoć jest to zamierzone, ale ja nie widzę żadnego pozytywu w tym, że git windowsowy chce nam na sile poprawić życie i mieli wszystkie pliki, które zdoła na okoliczność CRLF.
Prosta sztuczka, która temu zapobiegnie:
git config --global core.autocrlf false
Należy to zrobić przed sklonowaniem jakiegokolwiek repo. Wtedy od następnego clone'a linie będą już normalne.
A czemu jest to takie ważne? Ano temu, że przykładowo ściągając ArchiDroida z mojego githuba via git for windows nie uda nam się odpalić nawet aromy, z racji że spakujemy wersję z CRLF'ami, a nie LF'ami, a ani aroma ani android CRLF'ów nie czyta. Dlatego też jest to bardzo istotne i praktycznie pierwszy krok, który należy zrobić (dotyczy tylko git for windows).
Inna rzeczą, o której warto wspomnieć jest system plików. W linuxach standardowo jest ext4, w windowsach ntfs. No i dobra, można powiedzieć, że bez problemu da się kopiować jeden plik z ext4 na ntfs i vice versa, w końcu robimy to cały czas w naszym sgs3, ale czy na pewno?
Wykonaj test, utwórz na pulpicie plik test.txt, a następnie plik TEST.TXT. Niby litery są różne, a więc nie ma tej samej nazwy, no i co? 😃
No, to teraz będzie najlepsza część.
root@archi ~/git # touch a.txt
root@archi ~/git # touch A.TXT
root@archi ~/git # ls
a.txt A.TXT
Co ty na to? 😃
Powiesz sobie teraz "dobra, ale jak rzadko mamy styczność z taką sytuacją". Wbrew pozorom podczas tworzenia romów na androida bardzo często. A jak często? Pechowa liczba 13 plików o takich samych nazwach (ale różnych wielkościach liter) zawiera się w linuxowym kernelu 3.0.X. No i tutaj zonk bo jeśli gitem sklonujemy sobie repo linuxowego kernela 3.0.X to od razu na dzień dobry pojawi nam się 13 zmodyfikowanych plików, a przecież jeszcze nie zaczęliśmy nic zmieniać. No i w tym momencie całe repo można już wywalić bo jest popsute 😉. Poza tym, źródła Androida 4.4.X też NIE SĄ już zgodne z NTFS i linuxowy filesystem jest obowiązkowy.
To tylko przykłady niekompatybilności linuxa i windowsa, których można znaleźć o wiele więcej. Dlatego też trzeba pamiętać przynajmniej o tych dwóch rzeczach wyżej jeśli tworzy się rzeczy oparte o Androida. Nawet nie wiecie ile czasu straciłem na próby poszukiwania problemu gdy winą był nie kto inny, ale właśnie wymuszony CRLF, o istnieniu którego całkowicie zapomniałem w tamtym czasie.
NAJBEZPIECZNIEJSZĄ metodą na zabawę z romami jest działanie na jakimkolwiek linuxie z partycją ext4 (lub jakąkolwiek inną zgodną z case-insensitive), a do "shared" folderu windowsowego kopiowanie wyłącznie wyników końcowych, zipek, obrazów kernela itp.
Jeśli temat okaże się pomocny to na pewno dorzucę więcej ciekawostek w związku z zaawansowaną praca nad Androidem. Póki co nie proszę o podwieszenie tematu, ale jeśli któryś z moderatorów uzna, że temat jest tego wart to będę wdzięczny.
Z miłą chęcią również odpowiem na wszystkie pytania związane z "profesjonalnym" tworzeniem romów, te najbardziej podstawowe pytania proszę kierować do pozostałych tematów, jako że nie mam zbytnio sił na tłumaczeniu czym jest linux, jak zbudować CM etc. 🙂.