Una forma de automatizar la creación de ZIP flasheables

Entonces, mi pregunta es: ¿hay alguna manera de automatizar la creación de archivos ZIP flasheables, para evitar tener que escribir el script de actualización y el binario de actualización por mí mismo cada vez que quiero que se haga un ZIP?

Además, la única característica que se requiere que hagan los archivos ZIP es instalar una aplicación como aplicación del sistema . ¿Cómo podría realizar tal tarea, preferiblemente sin la ayuda de una aplicación externa?

Respuestas (2)

Al no poder encontrar algo que se ajustara a mis necesidades, decidí escribir un pequeño guión por mi cuenta. Está abierto a cualquiera que lo necesite.

#!/system/bin/sh

#This variable is used to calculate the time took by the various operations
#Altering it will result in badly calculated elapsed time
SECONDS=0

#This variable saves the path where the script resides
#DO NOT ALTER IT, or the whole script will fail with unpredictable errors
scriptDir="$(dirname "$(readlink -f "$0")")"

#This function verifies if there's at least an APK in the script's folder
#///Beginning of function "apkChecker"///
function apkChecker {
 echo "[INFO] Checking if at least an APK file is present..."
 if [ "$(ls "$scriptDir" | grep "\.apk$")" != "$(cat /dev/null)" ]; then
  echo "[INFO] At least one APK file has been found."
 else
  echo "[FATAL] No APK file found. Aborting."
  exit
 fi
}
#///End of function "apkChecker"///

#This function checks if the required tools (aapt and zip) are installed
#///Beginning of function "depencenciesChecker"///
function dependenciesChecker {
 echo "[INFO] Checking if both the aapt and zip utilities are installed..."

 whence -v aapt &> /dev/null
 isAaptAbsent="$(echo $?)"

 whence -v zip &> /dev/null
 isZipAbsent="$(echo $?)"

 case "$isAaptAbsent" in
  0)
   case "$isZipAbsent" in
    0)
     echo "[INFO] Both aapt and zip have been found."
     unset "isAaptAbsent" "isZipAbsent"
     ;;
    *)
     echo "[FATAL] zip cannot be found. Aborting."
     exit
     ;;
   esac
   ;;
  *)
   case "$isZipAbsent" in
    0)
     echo "[FATAL] aapt cannot be found. Aborting."
     exit
     ;;
    *)
     echo "[FATAL] Neither aapt nor zip can be found. Aborting."
     exit
     ;;
   esac
   ;;
 esac
}
#///End of function "dependenciesChecker"///

#This function relies on aapt, and is meant to retrieve the app's name from the given APK
#Notice that only the first APK in a list gets picked
#///Beginning of function "appNameRetriever"///
function appNameRetriever {
 echo "[INFO] Retrieving app name from the provided APK..."
 apkFile="$(ls "$scriptDir" | grep "\.apk$" | head -n 1)"
 appName="$(aapt d badging "$scriptDir"/"$apkFile")"
 appName="${appName#*application-label:\'}"
 appName="${appName//app*/}"
 appName="${appName%\'*}"
 displayedName=$appName
 appName="$(printf "%s" $appName)"
}
#///End of function "appNameRetriever"///

#This function creates the tree used by the ZIP (namely, the META-INF/com/google/android and appName folders)
#It also copies the previously picked APK to the appName folder, renaming it in the process
#///Beginning of function "folderTreeCreator"///
function folderTreeCreator {
 echo "[INFO] Creating ZIP folder tree and placing the APK to be flashed inside it..."
 mkdir -p ""$scriptDir"/$appName/META-INF/com/google/android"
 mkdir -p ""$scriptDir"/$appName/$appName"
 cp ""$scriptDir"/""$apkFile""" ""$scriptDir"/$appName/$appName/$appName.apk"
}
#///End of function "folderTreeCreator"///

#This function creates a dummy updater-script, which server mainly for aesthetical purposes
#///Beginning of function "updaterScriptCreator"///
function updaterScriptCreator {
 echo "[INFO] Creating dummy updater-script..."
 echo -n "#This is a dummy file. The magic is in the update-binary, which is a shell script." > ""$scriptDir"/$appName/META-INF/com/google/android/updater-script"
}
#///End of function "updaterScriptCreator"///

#=== BEGINNING OF "update-binary" WRITING SECTION ===
#From now on, the functions whose name begins with "sectionWriter", are used to assemble the update-binary

#This function writes the function and variables used for outputting things when flashing, to update-binary
#Thanks to Chainfire @ XDA, which seems to have been the first to implement them
#///Beginning of function "sectionWriter_header"///
function sectionWriter_header {
 echo "[INFO] Writing variables and functions prototypes to update-binary..."
 cat <<-EOF > ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
#!/sbin/sh

OUTFD=\$2
ZIP=\$3

ui_print() {
 echo -ne "ui_print \$1\n" > /proc/self/fd/\$OUTFD
 echo -ne "ui_print\n" > /proc/self/fd/\$OUTFD
}

EOF
}
#///End of function "sectionWriter_header"///

#This function writes the title of the ZIP (which contains the human-readable app name of the APK), to update-binary
#///Beginning of function "sectionWriter_zipName"///
function sectionWriter_zipName {
 echo "[INFO] Writing script title to update-binary..."
 cat << EOF >> ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
ui_print ""
ui_print "******************************"
ui_print " $displayedName Flashable ZIP "
ui_print " made by EDGAR, written by "
ui_print " Death Mask Salesman "
ui_print "******************************"
ui_print ""

EOF
}
#///End of function "sectionWriter_zipName"///

#This function writes the instructions necessary to mount the system partition in read-write mode, to update-binary
#///Beginning of function "sectionWriter_systemRwMount"///
function sectionWriter_systemRwMount {
 echo "[INFO] Writing mount instructions to update-binary..."
 cat << EOF >> ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
ui_print "~ Mounting /system in read-write mode..."
mount /system
mount -o remount,rw /system
ui_print ""

EOF
}
#///End of function "sectionWriter_systemRwMount"///

#This function writes the instructions to check for the Android version, to update-binary
#Notice that only JB, KK, LP and MM are supported, for now. That means a vast plethora of devices
#///Beginning of function "sectionWriter_osVersionChecker"///
function sectionWriter_osVersionChecker {
 echo "[INFO] Writing OS checking instructions to update-binary..."
 cat << EOF >> ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
ui_print "~ Checking Android version..."
androidVersion="\$(cat /system/build.prop | grep 'ro\.build\.version\.release')"
androidVersion="\${androidVersion#*=}"

case "\$androidVersion" in
 4.1*|4.1.*|4.2*|4.2.*|4.3*|4.3.*)
  ui_print "~ Android Jelly Bean (\$androidVersion) detected: proceeding."
  ;;
 4.4*|4.4.*)
  ui_print "~ Android KitKat (\$androidVersion) detected: proceeding."
  ;;
 5*|5.*|5.*.*)
  ui_print "~ Android Lollipop (\$androidVersion) detected: proceeding."
  ;;
 6*|6.*|6.*.*)
  ui_print "~ Android Marshmallow (\$androidVersion) detected: proceeding."
  ;;
 *)
  ui_print "~ Your Android version (\$androidVersion) is either obsolete or still unsupported."
  ui_print "~ Unmounting /system and aborting."
  umount /system
  exit
  ;;
esac

EOF
}
#///End of function "sectionWriter_osVersionChecker"///

#This function writes the instructions needed to unpack the ZIP to a temporary location, to update-binary
#This procedure is needed to obtain the APK to be installed as system app
#///Beginning of function "sectionWriter_zipUnpacker"///
function sectionWriter_zipUnpacker {
 echo "[INFO] Writing ZIP unpacking instructions to update-binary..."
 cat << EOF >> ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
ui_print "~ Extracting the ZIP to a temporary folder..."
mkdir -p "/tmp/$appName"
cd "/tmp/$appName"
unzip -o "\$ZIP"
ui_print ""

EOF
}
#///End of function "sectionWriter_zipUnpacker"///

#This function writes the bunch of instructions necessary to install the app as system, to update-binary
#As can be seen from the case-esac, almost each Android release has its specific install location
#///Beginning of function "sectionWriter_appInstaller"///
function sectionWriter_appInstaller {
 echo "[INFO] Writing APK installation instructions to update-binary..."
 cat << EOF >> ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
ui_print "~ Installing $displayedName..."

case "\$androidVersion" in
 4.1*|4.1.*|4.2*|4.2.*|4.3*|4.3.*)
  cp "/tmp/$appName/$appName/$appName.apk" "/system/app/"
  ;;
 4.4*|4.4.*)
  cp "/tmp/$appName/$appName/$appName.apk" "/system/priv-app/"
  ;;
 5*|5.*|5.*.*|6*|6.*|6.*.*)
  mkdir -p "/system/priv-app/$appName"
  cp "/tmp/$appName/$appName/$appName.apk" "/system/priv-app/$appName/"
  ;;
esac

ui_print "~ $displayedName has been installed."
ui_print ""

EOF
}
#///End of function "sectionWriter_appInstaller"///

#This function writes the instructions to change ownership, permissions and eventual SELinux context, to update-binary
#As for now, the script determines if SELinux is present by looking for the setenforce binary
#I'll probably base the check on the presence of "/sys/fs/selinux", but that has to be done in a next version 
#///Beginning of function "sectionWriter_validateApp"///
function sectionWriter_validateApp {
 echo "[INFO] Writing APK ownership, permission and context change instructions to update-binary..."
 cat << EOF >> ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
case "\$androidVersion" in
 4.1*|4.2*|4.3*)
  ui_print "~ Setting ownership and permissions..."
  chown 0.0 "/system/app/$appName.apk"
  chmod 644 "/system/app/$appName.apk"
  if [ -e "/system/bin/setenforce" ]; then
   ui_print "~ Setting SELinux appropriate context..."
   chcon u:object_r:system_file:s0 "/system/app/$appName.apk"
  fi
  ;;
 4.4*)
  ui_print "~ Setting ownership and permissions..."
  chown 0.0 "/system/priv-app/$appName.apk"
  chmod 644 "/system/priv-app/$appName.apk"
  if [ -e "/system/bin/setenforce" ]; then
   ui_print "~ Setting SELinux appropriate context..."
   chcon u:object_r:system_file:s0 "/system/priv-app/$appName.apk"
  fi
  ;;
 5*|6*)
  ui_print "~ Setting ownership and permissions..."
  chown 0.0 "/system/priv-app/$appName"
  chmod 755 "/system/priv-app/$appName"
  chown 0.0 "/system/priv-app/$appName/$appName.apk"
  chmod 644 "/system/priv-app/$appName/$appName.apk"
  if [ -e "/system/bin/setenforce" ]; then
   ui_print "~ Setting SELinux appropriate context..."
   chcon u:object_r:system_file:s0 "/system/priv-app/$appName"
   chcon u:object_r:system_file:s0 "/system/priv-app/$appName/$appName.apk"
  fi
  ;;
esac
ui_print ""

EOF
}
#///End of function "sectionWriter_validateApp"///

#This function writes the instructions to unmount the system partition, to update-binary
#///Beginning of function "sectionWriter_systemUnmount"///
function sectionWriter_systemUnmount {
 echo "[INFO] Writing unmount instructions to update-binary..."
 cat << EOF >> ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
ui_print "~ Unmounting /system..."
umount /system
ui_print ""

EOF
}
#///End of function "sectionWriter_systemUnmount"///

#This function writes something as a termination/goodbye message, to update-binary
#///Beginning of function "sectionWriter_goodbyeString"///
function sectionWriter_goodbyeString {
 echo "[INFO] Writing goodbye message to update-binary..."
 echo "ui_print \"Installation completed! Have a great day!\"" >> ""$scriptDir"/$appName/META-INF/com/google/android/update-binary"
}
#///End of function "sectionWriter_goodbyeString"///

#=== END OF "update-binary" WRITING SECTION ===

#This function packs the META-INF and appName folders, thus creating the flashable ZIP
#DO NOT set a compression level of "0", or the produced ZIP will be corrupted
#///Beginning of function "zipPacker"///
function zipPacker {
 echo "[INFO] Packing the ZIP..."

 cd ""$scriptDir"/$appName"
 zip -qr9 $appName.zip *
}
#///End of function "zipPacker"///

#This function moves the compressed ZIP to the same directory of the script, and wipes the directories created by "folderTreeCreator", along with their content
#///Beginning of function "cleanup"///
function cleanup {
 echo "[INFO] Moving the flashable ZIP to "$scriptDir"..."
 mv ""$scriptDir"/$appName/$appName.zip" "$scriptDir"

 echo "[INFO] Cleaning up the folders used as ZIP base..."
 rm -rf ""$scriptDir"/$appName"
}
#///End of function "cleanup"///

#This is the core of the EDGAR tool
#Here, all of the previously defined functions are called in the correct order
#To add a function, and thus extend EDGAR's versatility, simply define it above, and call it below
#///Beginning of EDGAR's core///
echo "**************************************"
echo " EDGAR - Flashable ZIP creator script "
echo "    by    Death    Mask    Salesman   "
echo "**************************************"
echo ""

apkChecker
echo ""

dependenciesChecker
echo ""

appNameRetriever
echo ""

folderTreeCreator
echo ""

updaterScriptCreator
echo ""

sectionWriter_header
sectionWriter_zipName
sectionWriter_systemRwMount
sectionWriter_osVersionChecker
sectionWriter_zipUnpacker
sectionWriter_appInstaller
sectionWriter_validateApp
sectionWriter_systemUnmount
sectionWriter_goodbyeString
echo ""

zipPacker
echo ""

cleanup
echo ""

echo "[INFO] Operations took "$(((SECONDS/60)/60))" hours, "$(((SECONDS/60)%60))" minutes and "$((SECONDS%60))" seconds."
echo "[INFO] All done!"
#///End of EDGAR's core///
¿Por qué hiciste de esto un wiki? ¿Te das cuenta de que su contenido está abierto a la alteración?
@Firelord Me doy cuenta completamente. Decidí convertirlo en un wiki porque me gustaría que los usuarios agregaran sus propias modificaciones (en caso de que fueran necesarias), correcciones y similares. En segundo lugar, no es justo que robe la reputación de algo que codifiqué y publiqué aprovechando una laguna en las reglas, incluso si puede ser útil para otros.
@DeathMaskSalesman No se trata realmente de robar reputación. La reputación es solo una recompensa por los buenos esfuerzos que ha realizado. Si desea que alguien corrija o mejore el guión, puede editar normalmente. Lo más probable es que se apruebe de todos modos
@DeathMaskSalesman, ¿puede explicar más sobre cómo usar esto, por favor? No entiendo dónde se ejecuta y cómo se usa. ¿Puede proporcionar un ejemplo de su uso, por favor? (De la ruta shebang, ¿supongo que se ejecuta en un dispositivo Android?) Además, ¿atiende a múltiples .apkpaquetes y atiende a paquetes que contienen bibliotecas nativas?
@starfry Debe ejecutarse desde un dispositivo Android rooteado. Suponga que su ubicación es el directorio raíz del almacenamiento interno. Deberá colocar su APK de elección en el mismo directorio del script, luego abrir una Terminal y emitir su. cdA continuación, /data/media/0asigne los permisos correctos con chmod 777 ./script_name.sh, donde script_nameestá el nombre que asignó a un archivo con el contenido de esta respuesta. Para crear con éxito un ZIP flasheable, su dispositivo deberá tener instalados los binarios aapt y zip . Una vez que se cumplan las condiciones, ejecute el script...
@starfry... con ./script_name.sh. El script ( EDGAR ) creará la estructura del ZIP flasheable, escribirá el script de actualización y el binario de actualización , copiará el APK en la carpeta apropiada del ZIP, comprimirá el ZIP actual y lo moverá al directorio del script y borrará el carpetas sin procesar que creó en el proceso. El script puede (a partir de ahora) crear ZIP flasheables de archivos APK independientes; sin bibliotecas. El ZIP per se puede actualizarse en dispositivos Android desde 4.1.xa 6.0.x (no se pudo probar Android N): su binario de actualización se encargará de todos los permisos y contextos de SELinux.
Bueno, ¡eso funcionó a la primera! Me tomaré un poco de tiempo para entenderlo y luego, con suerte, produciré una versión que maneje múltiples ZIPS e instale las bibliotecas en el lugar correcto. Te dejare saber como me va.
@starfry Me alegro de que haya funcionado y, por favor, mantenme informado sobre tu progreso. Tal vez voy a reescribir este, mientras tanto.
@DeathMaskSalesman Lo tengo ejecutándose en Linux (una vez que lo instalé aapt ) . El único cambio fue shebang ( /bin/sh) y usar whichen lugar de whence. Mañana intentaré hacerlo multi-paquete pero es suficiente por hoy!

Tomé el script de @DeathMaskSalesman y lo aumenté para que produzca un ZIP que contenga todos los .apkpaquetes que se encuentran en el directorio actual e instale tanto esos paquetes como las bibliotecas que contiene.

#!/bin/sh
#!/system/bin/sh
# Swap the above two lines as appropriate: Linux: /bin/sh; Android: /system/bin/sh
#http://android.stackexchange.com/questions/143304

#This variable is used to calculate the time took by the various operations
#Altering it will result in badly calculated elapsed time
SECONDS=0

#This variable saves the path where the script resides
#DO NOT ALTER IT, or the whole script will fail with unpredictable errors
scriptDir="$(dirname "$(readlink -f "$0")")"

# This defines the name of the created ZIP
ZIPFILE="$scriptDir/edgar.zip"

# This defines the name of the temporary directory where
# the ZIP structure will be built
ZIPDIR="$ZIPFILE.dir"

rm -rf "$ZIPDIR" "$ZIPFILE" # Uncomment to automatically remove pre-existing ZIP

#This defines the path to the update-binary within the ZIPDIR
UPDATE_BINARY="$ZIPDIR/META-INF/com/google/android/update-binary"

#Helper function to abort: prints fatal error message and exits
abort() { echo '[FATAL] '$*; exit 1; }

#Aborts unless listed dependencies are satisfied
depend() {
  for dep in "$@"
  do  
    which "${dep}" &> /dev/null || \
      abort 'Missing dependency '${dep}' (required: '$@')'
  done
}

#This function relies on aapt, and is meant to retrieve the app's name from the given APK
#///Beginning of function "appNameRetriever"///
#Sets globals $displayedName to the app name with whitespace intact
#             $appName to the app name with whitespace removed
#             $apkFile to the path passed in as $1
function appNameRetriever {
 echo "[INFO] Retrieving app name from $1..."
 apkFile="$1"
 appName="$(aapt d badging "$apkFile")"
 appName="${appName#*application-label:\'}"
 appName="${appName//app*/}"
 appName="${appName%\'*}"
 displayedName=$appName
 appName="$(printf "%s" $appName)"
}
#///End of function "appNameRetriever"///

#This function creates the directory structure required inside ZIP
init() {
  [[ -e "$1" ]] && abort "Cannot initialize '$1'; already exists"
  echo "[INFO] Initializing ZIP structure..."
  mkdir -p "$ZIPDIR/META-INF/com/google/android"
}
#///End of function "folderTreeCreator"///

add_apk() {
 echo "[INFO] Adding $appName..." 
 mkdir -p "$ZIPDIR/$appName"
 cp "$apkFile" "$ZIPDIR/$appName/$appName.apk"
}

#This function creates a dummy updater-script, which serves mainly for aesthetical purposes
#///Beginning of function "updaterScriptCreator"///
function updaterScriptCreator {
 echo "[INFO] Creating dummy updater-script..."
 echo -n "#This is a dummy file. The magic is in the update-binary, which is a shell script." > "$ZIPDIR/META-INF/com/google/android/updater-script"
}
#///End of function "updaterScriptCreator"///

#=== BEGINNING OF "update-binary" WRITING SECTION ===
#From now on, the functions whose name begins with "sectionWriter", are used to assemble the update-binary

#This function writes the function and variables used for outputting things when flashing, to update-binary
#Thanks to Chainfire @ XDA, which seems to have been the first to implement them
#///Beginning of function "sectionWriter_header"///
function sectionWriter_header {
 echo "[INFO] Writing variables and function prototypes to update-binary..."
 cat <<-EOF > "$ZIPDIR/META-INF/com/google/android/update-binary"
#!/sbin/sh

OUTFD=\$2
ZIP=\$3

ui_print() {
 echo -ne "ui_print \$1\n" > /proc/self/fd/\$OUTFD
 echo -ne "ui_print\n" > /proc/self/fd/\$OUTFD
}

get_buildprop() {
 prop=\$(grep "\${1//./\\.}=" /system/build.prop)
 echo "\${prop#*=}"
}

ui_print ""
ui_print "******************************"
ui_print " APK Flashable ZIP "
ui_print " made by EDGAR, written by "
ui_print " Death Mask Salesman "
ui_print "******************************"
ui_print ""
EOF
}
#///End of function "sectionWriter_header"///

#This function writes the title of the app (which contains the human-readable app name of the APK), to update-binary
#///Beginning of function "sectionWriter_zipName"///
function sectionWriter_appName {
 echo "[INFO] Writing script title to update-binary..."
 cat << EOF >> "$ZIPDIR/META-INF/com/google/android/update-binary"
ui_print ""
ui_print "******************************"

EOF
}
#///End of function "sectionWriter_zipName"///

#This function writes the instructions necessary to mount the system partition in read-write mode, to update-binary
#///Beginning of function "sectionWriter_systemRwMount"///
function sectionWriter_systemRwMount {
 echo "[INFO] Writing mount instructions to update-binary..."
 cat << EOF >> "$ZIPDIR/META-INF/com/google/android/update-binary"
ui_print "~ Mounting /system in read-write mode..."
mount /system
mount -o remount,rw /system
ui_print ""

EOF
}
#///End of function "sectionWriter_systemRwMount"///

#This function writes the instructions to check for the Android version, to update-binary
#Notice that only JB, KK, LP and MM are supported, for now. That means a vast plethora of devices
#///Beginning of function "sectionWriter_osVersionChecker"///
function sectionWriter_osVersionChecker {
 echo "[INFO] Writing OS checking instructions to update-binary..."
 cat << EOF >> "$ZIPDIR/META-INF/com/google/android/update-binary"
ui_print "~ Checking Android version..."
androidVersion="\$(get_buildprop 'ro.build.version.release')"

case "\$androidVersion" in
 4.1*|4.1.*|4.2*|4.2.*|4.3*|4.3.*)
  ui_print "~ Android Jelly Bean (\$androidVersion) detected: proceeding."
  ;;
 4.4*|4.4.*)
  ui_print "~ Android KitKat (\$androidVersion) detected: proceeding."
  ;;
 5*|5.*|5.*.*)
  ui_print "~ Android Lollipop (\$androidVersion) detected: proceeding."
  ;;
 6*|6.*|6.*.*)
  ui_print "~ Android Marshmallow (\$androidVersion) detected: proceeding."
  ;;
 *)
  ui_print "~ Your Android version (\$androidVersion) is either obsolete or still unsupported."
  ui_print "~ Unmounting /system and aborting."
  umount /system
  ;;
esac

EOF
}
#///End of function "sectionWriter_osVersionChecker"///

#This function gets the list of supported ABI versions (CPU Architecture)
#in increasing order of preference
#///Beginning of function "sectionWriter_getAbiList
function sectionWriter_getAbiList {
  echo "[INFO] Writing getAbi instructions to update-binary..."
  cat << EOF >> "$UPDATE_BINARY"
ui_print "~ Getting supported ABIs..."

reverse() {
  if [ "\$#" -gt 0 ]; then
    local arg=\$1
    shift
    reverse "\$@"
    echo -n "\$arg "
  fi
}

androidAbiList="\$(get_buildprop 'ro.product.cpu.abilist')"
androidAbiList="\${androidAbiList//,/ }"
if [[ -n "\$androidAbiList" ]]
then
  androidAbiList="\$(reverse \$androidAbiList)"
else
  androidAbi="\$(get_buildprop 'ro.product.cpu.abi')"
  androidAbi2="\$(get_buildprop 'ro.product.cpu.abi2')"
  androidAbiList="\$androidAbi2 \$androidAbi"
fi

ui_print "~ Supported ABIs: \$androidAbiList"

EOF
}
#///End of function "sectionWriter_osVersionChecker"///

#This function writes the bunch of instructions necessary to install the app as system, to update-binary
#As can be seen from the case-esac, almost each Android release has its specific install location
#///Beginning of function "sectionWriter_apkInstaller"///
function sectionWriter_apkInstaller {
 echo "[INFO] Writing APK installation instructions to update-binary..."
 cat << EOF >> "$ZIPDIR/META-INF/com/google/android/update-binary"
ui_print "~ Installing $displayedName..."

case "\$androidVersion" in
 4.1*|4.1.*|4.2*|4.2.*|4.3*|4.3.*)
  unzip -qo "\$ZIP" '$appName/$appName.apk' -d '/system/app/'
  ;;
 4.4*|4.4.*)
  unzip -qo "\$ZIP" '$appName/$appName.apk' -d '/system/priv-app/'
  ;;
 5*|5.*|5.*.*|6*|6.*|6.*.*)
  unzip -qo "\$ZIP" '$appName/*' -d '/system/priv-app'
  ;;
esac

ui_print "~ $displayedName has been installed."
ui_print ""

EOF
}
#///End of function "sectionWriter_apkInstaller"///

#This function writes instructions to update-binary to install any libs in the apk
#As can be seen from the case-esac, almost each Android release has its specific install location
#///Beginning of function "sectionWriter_libInstaller"///
function sectionWriter_libInstaller {
 echo "[INFO] Writing library installation instructions to update-binary..."
 cat << EOF >> "$ZIPDIR/META-INF/com/google/android/update-binary"
ui_print "~ Installing $displayedName libraries..."

case "\$androidVersion" in
 4.1*|4.1.*|4.2*|4.2.*|4.3*|4.3.*|4.4*|4.4.*)
  apkFile='/system/app/$appName/$appName.apk'
  ;;
 5*|5.*|5.*.*|6*|6.*|6.*.*)
  apkFile='/system/priv-app/$appName/$appName.apk'
  ;;
esac

unzip -qo "\$apkFile" 'lib/*'
for abi in \$androidAbiList
do
  if [[ -d "lib/\$abi" ]]
  then
    ui_print "~ \$abi..."
    mv lib/\$abi/* '/system/vendor/lib'
  fi
done
rm -rf lib

ui_print "~ $displayedName libs have been installed."
ui_print ""

EOF
}
#///End of function "sectionWriter_libInstaller"///

#This function writes the instructions to change ownership, permissions and eventual SELinux context, to update-binary
#As for now, the script determines if SELinux is present by looking for the setenforce binary
#I'll probably base the check on the presence of "/sys/fs/selinux", but that has to be done in a next version 
#///Beginning of function "sectionWriter_validateApp"///
function sectionWriter_validateApp {
 echo "[INFO] Writing APK ownership, permission and context change instructions to update-binary..."
 cat << EOF >> "$ZIPDIR/META-INF/com/google/android/update-binary"
case "\$androidVersion" in
 4.1*|4.2*|4.3*)
  ui_print "~ Setting ownership and permissions..."
  chown 0.0 "/system/app/$appName.apk"
  chmod 644 "/system/app/$appName.apk"
  if [ -e "/system/bin/setenforce" ]; then
   ui_print "~ Setting SELinux appropriate context..."
   chcon u:object_r:system_file:s0 "/system/app/$appName.apk"
  fi
  ;;
 4.4*)
  ui_print "~ Setting ownership and permissions..."
  chown 0.0 "/system/priv-app/$appName.apk"
  chmod 644 "/system/priv-app/$appName.apk"
  if [ -e "/system/bin/setenforce" ]; then
   ui_print "~ Setting SELinux appropriate context..."
   chcon u:object_r:system_file:s0 "/system/priv-app/$appName.apk"
  fi
  ;;
 5*|6*)
  ui_print "~ Setting ownership and permissions..."
  chown 0.0 "/system/priv-app/$appName"
  chmod 755 "/system/priv-app/$appName"
  chown 0.0 "/system/priv-app/$appName/$appName.apk"
  chmod 644 "/system/priv-app/$appName/$appName.apk"
  if [ -e "/system/bin/setenforce" ]; then
   ui_print "~ Setting SELinux appropriate context..."
   chcon u:object_r:system_file:s0 "/system/priv-app/$appName"
   chcon u:object_r:system_file:s0 "/system/priv-app/$appName/$appName.apk"
  fi
  ;;
esac
ui_print ""

EOF
}
#///End of function "sectionWriter_validateApp"///

#This function writes the instructions to unmount the system partition, to update-binary
#///Beginning of function "sectionWriter_systemUnmount"///
function sectionWriter_systemUnmount {
 echo "[INFO] Writing unmount instructions to update-binary..."
 cat << EOF >> "$UPDATE_BINARY"
ui_print "~ Unmounting /system..."
umount /system
ui_print ""

EOF
}
#///End of function "sectionWriter_systemUnmount"///

#This function writes something as a termination/goodbye message, to update-binary
#///Beginning of function "sectionWriter_goodbyeString"///
function sectionWriter_goodbyeString {
 echo "[INFO] Writing goodbye message to update-binary..."
 echo "ui_print \"Installation completed! Have a great day!\"" >> "$UPDATE_BINARY"
}
#///End of function "sectionWriter_goodbyeString"///

#=== END OF "update-binary" WRITING SECTION ===

#This function packs the prepared ZIP directory and
#removes it, leaving behind the flashable ZIP
#DO NOT set a compression level of "0", or the produced ZIP will be corrupted
#///Beginning of function "zipPacker"///
function zipPacker {
 echo "[INFO] Packing the ZIP..."

 OLDPWD=$(pwd)
 cd "$ZIPDIR"
 zip -qr9 "$ZIPFILE" .
 cd "$OLDPWD"
 rm -rf "$ZIPDIR"
}
#///End of function "zipPacker"///

#This is the core of the EDGAR tool
#Here, all of the previously defined functions are called in the correct order
#To add a function, and thus extend EDGAR's versatility, simply define it above, and call it below
#///Beginning of EDGAR's core///
echo "**************************************"
echo " EDGAR - Flashable ZIP creator script "
echo "    by    Death    Mask    Salesman   "
echo "**************************************"
echo ""

depend aapt zip find
echo ""

init "$ZIPDIR"
echo ""

updaterScriptCreator
echo ""

sectionWriter_header
sectionWriter_systemRwMount
sectionWriter_osVersionChecker
sectionWriter_getAbiList

for appName in $( find "$scriptDir" -maxdepth 1 -type f -name '*.apk' )
do
  appNameRetriever "$appName"
  sectionWriter_appName
  add_apk
  sectionWriter_apkInstaller
  sectionWriter_libInstaller
  sectionWriter_validateApp
done

sectionWriter_systemUnmount
sectionWriter_goodbyeString
echo ""

zipPacker
echo ""

echo "[INFO] Operations took "$(((SECONDS/60)/60))" hours, "$(((SECONDS/60)%60))" minutes and "$((SECONDS%60))" seconds."
echo "[INFO] All done!"
#///End of EDGAR's core///

Algunos puntos

  • El script se ejecuta en mi Android Lollipop y en Linux. No he podido probar en otras versiones de Android.
  • Solo he probado flashear el ZIP generado usando FlashFire.
  • Instala bibliotecas para la ABI que el sistema Android informa como válidas. Donde hay varios, los instala todos desde el menos preferible hasta el más preferible (por lo que este último sobrescribe al primero).

Estoy aprendiendo sobre el funcionamiento interno de Android y es posible que haya hecho algunas cosas incorrectamente, así que tenlo en cuenta cuando mires el script. Creo que al guión le vendría bien un poco de limpieza, pero solo quería publicarlo para que @DeathMaskSalesman lo viera.

Parece bastante bueno, diría yo. ¡Lo probaré cuando mi otro script, NEMRIS, sea como yo quiero! Y felicitaciones por hacerlo ejecutable bajo Linux con un mínimo esfuerzo.
Hola, @DeathMaskSalesman, realicé algunas modificaciones en mi secuencia de comandos para admitir instalaciones tanto para /system/appy /system/priv-appcomo para admitir la inyección de comandos personalizados en torno a la instalación (por ejemplo, para borrar aplicaciones de stock no deseadas). He subido mi última versión a GitHub