2 votes

Existe-t-il un outil non applicatif pour la sauvegarde par lots des fichiers APK ?

Comme les autres fois, une réponse à ma question ne doit pas contempler une application du Play Store ou de toute autre source. Ce que je recherche, c'est un outil capable de sauvegarder applications utilisateur , applications système ou ces deux catégories lorsqu'il est lancé à partir d'un système d'exploitation Android fonctionnel.

Sauvegarde des données externes des applications n'est pas pertinent . La seule chose qui compte, c'est la possibilité de sauvegarder par lots les base.apk que l'on trouve dans chaque /data/app/appName/ afin que je puisse le réinstaller, soit pour revenir à une version antérieure, soit pour l'installer à partir de zéro.

Enfin, je préférerais que l'outil soit librement personnalisable (c'est-à-dire qu'il ne soit pas compilé). Peut-être quelque chose comme un script shell ?

5voto

Grimoire Points 2908

Ce script est obsolète et n'est plus pris en charge. Pour la version la plus récente, qui nécessite Android 5+ et Python 3.5+, et qui prend en charge les variantes du thème Arcus et les superpositions Substratum, voir mon site web Dépôt GitHub .


Voici mon scénario remanié, désormais doublé NEMRIS ce qui répond à la question. Comme d'habitude, il est librement modifiable par n'importe qui, et il est également compatible avec sh, ce qui signifie qu'il peut être exécuté à partir de l'interpréteur de commandes Android.

En fait, le script dépend de l'option aapt peut discerner si l'utilitaire est installé, est capable de déterminer si l'utilitaire est installé, est capable de déterminer si l'utilitaire est installé, est capable de déterminer si l'utilitaire est installé. backupDir et le appsToBackup sont vides ou non et, si c'est le cas, il définit des valeurs par défaut au lieu d'abandonner.

Cependant, la date binaire ne peut pas déterminer votre véritable fuseau horaire, c'est pourquoi tout le monde doit modifier manuellement son TZ variable.

Une fois lancé pour la première fois, l'outil est capable de déterminer si un APK a déjà été sauvegardé ou non, en utilisant un tableau rempli de sommes de contrôle MD5.

Il stocke également ce tableau dans un dictionnaire, appelé md5sums.txt qui est un fichier en clair situé dans le répertoire de sauvegarde de l'APK. Cette fonctionnalité permet de réduire de moitié le temps écoulé, mais elle dépend d'une statut binaire capable de mesurer la taille exacte d'un fichier en octets. Si le statut n'est pas conforme, les sommes de contrôle seront calculées à chaque fois, comme dans la version précédente du script. Cela permet de s'assurer que NEMRIS ne dépendra que de aapt . N'oubliez pas d'effacer manuellement le md5sums.txt si vous faites un peu de nettoyage dans le dossier de sauvegarde.

En outre, le script dispose désormais d'un maxDictSize qui accepte une valeur entière. La valeur saisie sera interprétée comme des octets. Cette valeur peut être personnalisée et sert à recalculer le dictionnaire des sommes de contrôle si sa taille dépasse le maximum autorisé.

Enfin, je l'ai patché pour qu'il prenne en charge les applications tierces dont les noms contiennent des barres obliques. Plus d'erreurs bizarres, je pense.

J'espère qu'il sera utile.

Licence : Licence publique générale GNU, version 3 ou plus tard.


#!/system/bin/sh

# nemris - an Android app backup tool

# Copyright (C) 2016 Death Mask Salesman
# <http://android.stackexchange.com/users/152843/death-mask-salesman>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License at <http://www.gnu.org/licenses/> for
# more details.

#This variable is used to calculate the total elapsed time
#DO NOT alter it, or the script won't be able to calculate it
SECONDS=0

#This variable is used to output the time when the script is launched
#Edit the plus/minus and the value after "UTC", to force date into displaying your correct time zone
timeAtStart="$(TZ=UTC-2 date +%H:%M:%S)"

#This variable stores the path where to backup your apps' APK files
#Edit it freely, but do not forget the quotes
backupDir=""

#This variable stores the path where the script is located
scriptDir="$(dirname "$(readlink -f "$0")")"

#This variable tells the script which apps to backup
#Supported values are:
# - "User": tells the script to backup only third-party apps
# - "System": tells the script to backup only system apps
# - "All": tells the script to backup both 3rd party and system apps
appsToBackup=""

#This variable contains the maximum size of the checksums dictionary, in bytes
#It is used to decide whether to flush the dictionary or not
typeset -i maxDictSize=20000

#This function outputs the time when the operations start. Purely cosmetic: can be disabled safely
#///Beginning of function "timeInfo"///
function timeInfo {
 echo "[ INFO ] Operations started at "$timeAtStart"."
}
#///End of function "timeInfo"///

#This function checks if the "aapt" and "stat" utilities are installed
#///Beginning of function "dependencyChecker"///
function dependencyChecker {
 echo "[ INFO ] Checking if aapt and stat are installed..."
 whence -v aapt &> /dev/null
 isAaptAbsent="$(echo $?)"

 whence -v stat &> /dev/null
 isStatAbsent="$(echo $?)"

 if [ "$isAaptAbsent" == "0" ]; then
  echo "[ INFO ] aapt has been found."
  unset isAaptAbsent
 else
  echo "[ FATAL ] aapt cannot be found. Aborting."
  exit
 fi

 if [ "$isStatAbsent" == "0" ]; then
  echo "[ INFO ] stat has been found."
  echo ""
  echo "[ INFO ] Checking if stat can measure exact file sizes..."

  stat -c %s "$scriptDir/nemris.sh" &> /dev/null
  if [ "$(echo $?)" == "0" ]; then
   echo "[ INFO ] Measurement successful."
   isStatUsable="1"
   unset isStatAbsent
  else
   echo "[ WARNING ] Measurement failed. stat will be ignored."
   isStatUsable="0"
   unset isStatAbsent
  fi
 else
  echo "[ WARNING ] stat cannot be found."
  isStatUsable="0"
  unset isStatAbsent
 fi
}
#///End of function "dependencyChecker"///

#This function verifies whether the values into "backupDir" and "appsToBackup" have anything inside or not
#In the case that the variables are empty, default values will be used, instead
#///Beginning of the function "variablesChecker"///
function variablesChecker {
 echo "[ INFO ] Checking if a backup path has already been specified..."
 if [ "$backupDir" == "$(cat /dev/null)" ]; then
  echo "[ WARNING ] A backup path has not been specified."
  echo "[ WARNING ] Setting the path to default (/sdcard/AppsBackup)."
  backupDir="/sdcard/AppsBackup"
 else
  echo "[ INFO ] Backup path has already been specified."
 fi
 echo ""

 echo "[ INFO ] Checking if the typology of apps to backup has already been chosen..."
 if [ "$appsToBackup" == "$(cat /dev/null)" ]; then
  echo "[ WARNING ] A typology of apps to backup has not been specified."
  echo "[ WARNING ] Setting the default typology (User)."
  appsToBackup="User"
 else
  echo "[ INFO ] A typology of apps has already been specified."
 fi
}
#///End of the function "variablesChecker"///

#This function emulates the case insensitivity for "appsToBackup", usually provided by brace expansion, but lacked by sh
#In the case that an unsupported value is found, a default value will be used instead
#///Beginning of the function "caseInsensitiveWorkaround"///
function caseInsensitiveWorkaround {
 systemArray="$(echo -n {s,S}{y,Y}{s,S}{t,T}{e,E}{m,M})"
 userArray="$(echo -n {u,U}{s,S}{e,E}{r,R})"
 allArray="$(echo -n {a,A}{l,L}{l,L})"

 case ${appsToBackup:0:1} in
  s|S)
   for i in $systemArray; do
    case "$appsToBackup" in
     $i)
      appsToBackup="System"
      ;;
    esac
   done
   ;;
  u|U)
   for i in $userArray; do
    case "$appsToBackup" in
     $i)
      appsToBackup="User"
      ;;
    esac
   done
   ;;
  a|A)
   for i in $allArray; do
    case "$appsToBackup" in
     $i)
      appsToBackup="All"
      ;;
    esac
   done
   ;;
  *)
   echo "[ WARNING ] \""$appsToBackup"\": invalid typology."
   echo "[ WARNING ] Setting the default typology (User)."
   appsToBackup="User"
   ;;
 esac

 case "$appsToBackup" in
  System|User|All)
   ;;
  *)
   echo "[ WARNING ] \""$appsToBackup"\": invalid typology."
   echo "[ WARNING ] Setting the default typology (User)."
   appsToBackup="User"
   ;;
 esac
}
#///End of the function "caseInsensitiveWorkaround"///

#This function checks if the backup directory already exists
#///Beginning of the function "backupDirCheck"///
function backupDirCheck {
 echo "[ INFO ] Checking if the backup directory already exists..."
 if [ -d "$backupDir" ]; then
  echo -n "[ INFO ] Backup directory already exists, "

  cd "$backupDir"
  #echo "[ INFO ] Checking if the directory is empty..."
  if [ "$(ls | grep "\.apk$" | head -n 1)" == "$(cat /dev/null)" ]; then
   echo "and does not have any APK file inside."
  else
   echo "and has at least an APK file inside."
   isDirectoryEmpty="0"
  fi
 else
  echo "[ INFO ] Backup directory does not exist. Creating it now."
  mkdir -p "$backupDir"
  cd "$backupDir"
 fi
}
#///End of the function "backupDirCheck"///

#This function compares the size of the checksums dictionary and flushes it if too big
#///Beginning of function "sizeComparer"///
function sizeComparer {
 echo "[ INFO ] Comparing the dictionary size to the max value..."

 typeset -i dictSize="$(stat -c %s "$backupDir/md5sums.txt")"
 if [ $dictSize -ge maxDictSize ]; then
  echo "[ INFO ] The dictionary size exceeds the maximum value. The checksums will be recomputed."
  dictTooBig="1"
  unset dictSize
 else
  echo "[ INFO ] The dictionary size does not exceed the maximum value."
 fi
}
#///End of function "sizeComparer"///

#This function generates a MD5 array, filled with the checksum of each APK file
#///Beginning of the function "checksumsGenerator"///
function checksumsGenerator {
 echo "[ INFO ] Generating file checksums..."
 apkList="$(printf "%s\n" * | grep "\.apk$")"
 for i in $apkList; do
  tempMd5=($(md5sum $i))
  md5Array="$tempMd5 $md5Array"
 done

 cat << EOF > "$backupDir/md5sums.txt"
md5Array="$md5Array"
EOF

 echo "[ INFO ] MD5 array generated."
}
#///End of the function "checksumsGenerator"///

#This function creates a list of the apps to backup, based on the content in "appsToBackup"
#///Beginning of the function "appListGenerator"///
function appListGenerator {
 case "$appsToBackup" in
  System)
   echo "[ INFO ] Retrieving system apps list..."
   appList="$(pm list packages -s)"
   ;;
  User)
   echo "[ INFO ] Retrieving third-party apps list..."
   appList="$(pm list packages -3)"
   ;;
  All)
   echo "[ INFO ] Retrieving apps list..."
   appList="$(pm list packages)"
   ;;
 esac

 echo "[ INFO ] Applications list retrieved."
}
#///End of the function "appListGenerator"///

#This function creates an array, filled with each app's full path
#///Beginning of the function "apkPathRetriever"///
function apkPathRetriever {
 echo "[ INFO ] Collecting the path of each app's APK..."
 for i in $appList; do
  apkPath="${i#p*:}"
  apkPath="$(pm path $apkPath)"
  apkPath="${apkPath#p*:}"
  pathArray="$apkPath $pathArray"
 done
 echo "[ INFO ] Paths collected."
}
#///End of the function "apkPathRetriever"///

#This function extracts the label or the package name for the APK supplied to it
#It extracts the app label for any third-party app, the package name for any system app
#///Beginning of the function "appNameRetriever"///
function appNameRetriever {
 case "$1" in
  /system/*)
   appName="${aaptOutput#*name=\'}"
   appName="${appName//\'*/}"
   displayedName="$appName"
   ;;
  /data/*|/mnt/asec/*)
   appName="${aaptOutput#*application-label:\'}"
   appName="${appName//app*/}"
   appName="${appName%\'*}"
   displayedName=$appName
   appName="${appName//\//}"
   appName="$(printf "%s" $appName)"
   ;;
 esac
}
#///End of the function "appNameRetriever"///

#This function retrieves the version number of the APK
#///Beginning of the function "appVersionRetriever"///
function appVersionRetriever {
 appVersion="${aaptOutput#*versionName=\'}"
 appVersion="${appVersion//platformBuildVersion*/}"
 appVersion="${appVersion%\'*}"
 appVersion="$(printf "%s" $appVersion)"
}
#///End of the function "appVersionRetriever"///

#This function compares the MD5 of the APK in question against any MD5 in the array
#If it finds a match, then the function exits and the APK isn't backed up
#///Beginning of the function "md5Compare"///
function md5Compare {
 alreadyBackedUp=0
 appMd5=($(md5sum "$1"))

 for c in $md5Array; do
  if [ "$alreadyBackedUp" == "0" ]; then
   if [ "$appMd5" == "$c" ]; then
    alreadyBackedUp=1;
   fi
  else
   break;
  fi
 done
}
#///End of the function "md5Compare"///

#This function decides whether to backup an app or not
#It calls both "appNameRetriever" and "appVersionRetriever" for renaming any copied "base.apk"
#It also decides whether to call "md5Compare" or not, in order to see if an app has already been backed up
#///Beginning of the function "apkBackup"///
function apkBackup {
 for i in $pathArray; do
  aaptOutput="$(aapt d badging "$i")"

  appNameRetriever "$i"
  appVersionRetriever

  if [ "$isDirectoryEmpty" == "0" ]; then
   md5Compare "$i"

   if [ "$alreadyBackedUp" == "0" ]; then
    echo -n "[ INFO ] Backing up "$displayedName"... "
    cp "$i" "$backupDir"/$appName"_"$appVersion.apk
    echo "done."
    md5Array="$appMd5 $md5Array"
   else
    echo "[ INFO ] "$displayedName" has already been backed up."
   fi
  else
   echo -n "[ INFO ] Backing up "$displayedName"... "
   cp "$i" "$backupDir"/$appName"_"$appVersion.apk
   echo "done."
  fi
 done

 if [ "$md5Array" != "$(cat /dev/null)" ]; then
  cat << EOF > "$backupDir/md5sums.txt"
md5Array="$md5Array"
EOF
 fi
}
#///End of the function "apkBackup"///

#This is the core of the tool
echo "**************************"
echo " NEMRIS - App backup tool "
echo "  by Death Mask Salesman  "
echo "**************************"

timeInfo
echo ""

dependencyChecker
echo ""

variablesChecker
echo ""

caseInsensitiveWorkaround
echo ""

backupDirCheck
echo ""

#This section checks whether to analyze the dictionary size
#It also decides whether to compute the checksums
if [ "$isDirectoryEmpty" == "0" ]; then
 if [ -e "$backupDir/md5sums.txt" ]; then
  if [ "$isStatUsable" == "1" ]; then
   sizeComparer
   echo ""
  else
   dictTooBig="1"
  fi

  if [ "$dictTooBig" == "1" ]; then
   checksumsGenerator
   echo ""
  else
   source "$backupDir/md5sums.txt"
  fi  
 else
  echo "[ INFO ] Checksums not calculated yet."
  checksumsGenerator
  echo ""
 fi
fi

appListGenerator
echo ""

apkPathRetriever
echo ""

apkBackup
echo ""

echo "[ INFO ] Operations took "$(((SECONDS/60)/60))" hours, "$(((SECONDS/60)%60))" minutes and "$((SECONDS%60))" seconds."
echo "[ INFO ] All done!"

1voto

Grimoire Points 2908

Il s'agit d'une version particulière et améliorée de la norme Nemris l'outil. Il constitue une réponse distincte car, bien qu'il permette une amélioration considérable de la vitesse, il n'a pas été testé (il fonctionne avec certitude dans les cas suivants CM12.1 y CM13 ).


Licence

Cet outil est placé sous la licence WTFPL. En d'autres termes, faites-en ce que vous voulez. Réutilisez ses composants, concédez des sous-licences... et citez-moi comme auteur original, si vous le souhaitez. Je ne prends cependant aucune responsabilité.


Différences par rapport à l'ancien Nemris

  • La verbosité de l'outil a été réduite.
  • n'est plus nécessaire.
  • La vitesse de sauvegarde a été considérablement améliorée : une sauvegarde delta prend environ 25 secondes, au lieu d'une minute.
  • Correction du bug de la sauvegarde des applications système en ignorant aapt.
  • Ajout de la prise en charge de la sauvegarde des applications désactivées.
  • Deux arguments de ligne de commande facultatifs ont été ajoutés ; leur utilisation est décrite ci-dessous.

Répertoire de sauvegarde par défaut

Pour spécifier un répertoire de sauvegarde personnalisé, modifiez manuellement ligne 17 . Si vous laissez ce champ vide, le script passera par défaut à /sdcard/NemrisBackup .


Utilisation

Nemris peut être lancé soit sans arguments, soit avec deux arguments optionnels. Utilisation :

nemris.sh [app_typology] [reset]

. Le premier argument peut être Utilisateur , Système , Handicapés ou Tous ou même être laissée en blanc. Si l'argument fourni n'est pas reconnu ou est nul, l'outil utilise par défaut l'option Utilisateur . L'insensibilité à la casse est prise en charge.

Le deuxième argument ne peut être que réinitialiser et ne peut être spécifié que si le premier argument a également été spécifié. Dans ce cas, l'outil supprimera et recréera ses fichiers de configuration, qui se trouvent au même endroit que le script.


#!/system/bin/sh

# A bunch of variables and a function that facilitate the script's workings
# Do not alter them unless you know what you're doing
SECONDS=0
app_typology="$1"
null="$(cat /dev/null)"
script_location="$(dirname "$(readlink -f "$0")")"

function var_to_file {
 echo "$1=\"$2\"" >> "$script_location/nemris_config.txt"
}

####################

# This variable is needed to define the backup location
backup_location=""

####################

# This function cleans up the files generated in a previous execution
function settings_md5_files_delete {
 echo -ne "[ INFO ] Deleting Nemris configuration and MD5 dictionary..."

 if [ -e "$script_location/nemris_config.txt" ]; then
  if [ -e "$script_location/md5_dictionary.txt" ]; then
   rm "$script_location/nemris_config.txt" "$script_location/md5_dictionary.txt"
  else
   rm "$script_location/nemris_config.txt"
  fi
 else
  if [ -e "$script_location/md5_dictionary.txt" ]; then
   rm "$script_location/md5_dictionary.txt"
  else
   echo -ne " no files to remove..."
  fi
 fi

 echo " done. The files will be regenerated."
}

# This function checks if a settings file has been generated from a previous execution and loads its data
function settings_file_check {
 echo -ne "[ INFO ] Checking for Nemris configuration file..."

 if [ -e "$script_location/nemris_config.txt" ]; then
  echo " found."

  if [ "$backup_location" == "$null" ]; then
   source "$script_location/nemris_config.txt"
  else
   temp="$backup_location"
   source "$script_location/nemris_config.txt"
   backup_location="$temp"

   unset temp
  fi
 else
  echo " not found. The configuration will be regenerated."

  touch "$script_location/nemris_config.txt"
 fi
}

# This function reads the list of the packages and generates a special dictionary
# This is the reason of the time gain between the legacy Nemris and this version
function packages_file_write {
 echo -ne "[ INFO ] Generating refined packages list..."

 if [ -e "$script_location/packages.txt" ]; then
  echo -n "$null" > "$script_location/packages.txt"
 fi

 cat "/data/system/packages.xml" | grep "package name" > "$script_location/packages.txt"

 IFS=$'\n'

 packages="$(cat "$script_location/packages.txt")"
 echo -n "$null" > "$script_location/packages.txt"

 for i in $packages; do
  package_name="${i//*package name=\"/}"
  package_name="${package_name//\"*/}"

  package_path="${i//*codePath=\"/}"
  package_path="${package_path//\"*/}"

  if [ "$(echo -ne "$package_path" | grep "\.apk$")" == "$null" ]; then
   package_path="$package_path/$(ls "$package_path" | grep "\.apk$")"
  fi

  echo "$package_name:$package_path" >> "$script_location/packages.txt"
 done

 unset IFS package package_name package_path

 echo " done."
}

# This function checks if aapt is installed
function aapt_check {
 echo -ne "[ INFO ] Checking for aapt..."

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

 var_to_file "is_aapt_missing" "$is_aapt_missing"

 echo " done."
}

# This function sets the backup location and the app typology to backup to default
function variables_default {
 case $1 in
  0)
   echo -ne "[ INFO ] Setting backup directory and app typology to default..."

   backup_location="/sdcard/NemrisBackup"
   app_typology="User"

   var_to_file "backup_location" "$backup_location"

   echo " done."
   ;;
  1)
   echo -ne "[ INFO ] Setting backup location to default..."

   backup_location="/sdcard/NemrisBackup"

   var_to_file "backup_location" "$backup_location"

   echo " done."
   ;;
  2)
   echo -ne "[ INFO ] Setting app typology to default..."

   app_typology="User"

   echo " done."
   ;;
 esac
}

# This function ensures case insensitivity and saves a headache to the user
function app_typology_sanitize {
 case ${app_typology:0:1} in
  s|S)
   if [ "$system_permutations" == "$null" ]; then
    permutations_already_calculated=0
    system_permutations="$(echo -n {s,S}{y,Y}{s,S}{t,T}{e,E}{m,M})"
   fi

   for i in $system_permutations; do
    if [ "$app_typology" == "$i" ]; then
     app_typology="System"
     break
    fi
   done

   if [ "$permutations_already_calculated" == "0" ]; then
    var_to_file "system_permutations" "$system_permutations"
   fi
   ;;
  u|U)
   if [ "$user_permutations" == "$null" ]; then
    permutations_already_calculated=0
    user_permutations="$(echo -n {u,U}{s,S}{e,E}{r,R})"
   fi

   for i in $user_permutations; do
    if [ "$app_typology" == "$i" ]; then
     app_typology="User"
     break
    fi
   done

   if [ "$permutations_already_calculated" == "0" ]; then
    var_to_file "user_permutations" "$user_permutations"
   fi
   ;;
  d|D)
   if [ "$disabled_permutations" == "$null" ]; then
    permutations_already_calculated=0
    disabled_permutations="$(echo -n {d,D}{i,I}{s,S}{a,A}{b,B}{l,L}{e,E}{d,D})"
   fi

   for i in $disabled_permutations; do
    if [ "$app_typology" == "$i" ]; then
     app_typology="Disabled"
     break
    fi
   done

   if [ "$permutations_already_calculated" == "0" ]; then
    var_to_file "disabled_permutations" "$disabled_permutations"
   fi
   ;;
  a|A)
   if [ "$all_permutations" == "$null" ]; then
    permutations_already_calculated=0
    all_permutations="$(echo -n {a,A}{l,L}{l,L})"
   fi

   for i in $all_permutations; do
    if [ "$app_typology" == "$i" ]; then
     app_typology="All"
     break
    fi
   done

   if [ "$permutations_already_calculated" == "0" ]; then
    var_to_file "all_permutations" "$all_permutations"
   fi
   ;;
  System|User|Disabled|All)
   ;;
  *)
   echo -ne "[ WARN ] \"$app_typology\": invalid value. Setting to default (User)..."

   app_typology="User"

   echo " done."
   ;;
 esac

 case $app_typology in
  System|User|Disabled|All)
   ;;
  *)
   echo -ne "[ WARN ] \"$app_typology\": invalid value. Setting to default (User)..."

   app_typology="User"

   echo " done."
   ;;
 esac
}

# This function checks if the backup directory contains any APK
function backup_location_check {
 if [ -d "$backup_location" ]; then
  if [ "$(ls "$backup_location" | grep "\.apk$")" == "$null" ]; then
   backup_directory_is_empty=1
  else
   backup_directory_is_empty=0
  fi
 else
  echo -ne "[ INFO ] Creating backup directory..."

  mkdir -p "$backup_location"

  echo " done."
 fi

 cd "$backup_location"
}

# This function generates a MD5 checksums dictionary
# This shortens down the operations time in any execution of the script
function md5_dict_generate {
 echo -ne "[ INFO ] Generating MD5 checksums..."

 apk_list="$(printf "%s\n" * | grep "\.apk$")"

 for i in $apk_list; do
  temp=($(md5sum "$i"))
  md5_array="$temp $md5_array"
 done

 echo -n "md5_array=\""$md5_array"\"" > "$script_location/md5_dictionary.txt"

 echo " done."
}

# This function retrieves the list of installed apps
function apps_list_retrieve {
 case $app_typology in
  System)
   echo -ne "[ INFO ] Retrieving system apps list..."

   for i in $(pm list packages -s); do
    pkg="${i//p*:/}"
    pkg="${pkg//./\\.}"
    apps_list="$apps_list $pkg"
   done

   echo " done."
   ;;
  User)
   echo -ne "[ INFO ] Retrieving third-party apps list..."

   for i in $(pm list packages -3); do
    pkg="${i//p*:/}"
    pkg="${pkg//./\\.}"
    apps_list="$apps_list $pkg"
   done

   echo " done."
   ;;
  Disabled)
   echo -ne "[ INFO ] Retrieving disabled apps list..."

   for i in $(pm list packages -d); do
    pkg="${i//p*:/}"
    pkg="${pkg//./\\.}"
    apps_list="$apps_list $pkg"
   done

   echo " done."
   ;;
  All)
   echo -ne "[ INFO ] Retrieving apps list..."

   for i in $(pm list packages); do
    pkg="${i//p*:/}"
    pkg="${pkg//./\\.}"
    apps_list="$apps_list $pkg"
   done

   echo " done."
   ;;
 esac
}

# This function retrieves the APKs paths via their package name (from the dictionary)
# Actually, this is the second part of the magic
function apks_paths_retrieve {
 echo -ne "[ INFO ] Collecting the path of each app's APK..."

 for i in $apps_list; do
  row="$(cat "$script_location/packages.txt" | grep "$i:")"
  paths_list="$paths_list ${row//*:/}"
 done

 echo " done."
}

# This function retrieves the app name
# If an app has no name, it falls back to the package name
function app_name_retrieve {
 app_name="$(echo -ne "$aapt_output" | grep "application-label:\'")"

 if [ "$app_name" != "$null" ]; then
  app_name="${app_name//application-label:\'/}"
  app_name="${app_name%\'*}"
  displayed_name="$app_name"
  app_name="${app_name//\//}"
  app_name="$(printf "%s" $app_name)"
 else
  app_name="$(echo -ne "$aapt_output" | grep "package: name.")"
  app_name="${app_name//package: name=\'/}"
  app_name="${app_name//\'*/}"
  displayed_name="$app_name"
 fi
}

# This function retrieves the app version
# If an app has no version (weird, but that happens) it defaults to "None"
function app_version_retrieve {
 app_version="${aapt_output#*versionName=\'}"
 app_version="${app_version//platformBuildVersion*/}"
 app_version="${app_version%\'*}"
 app_version="$(printf "%s" $app_version)"

 if [ "$app_version" == "$null" ]; then
  app_version="None"
 fi
}

# This function sets the app name to the name of the APK file
# It's used when the app to be backed up is a system app, which may make aapt go crazy
function system_app_name_set {
 app_name="$(basename $1)"
 app_name="${app_name%\.apk}"
 displayed_name="$app_name"
}

# This function checks if the checksum of any app figures in the already backed up apps
# This way, any app is backed up exactly once, and Nemris cannot be fooled by file names
function md5_compare {
 already_backed_up=0
 app_md5=($(md5sum "$1"))

 for c in $md5_array; do
  if [ "$already_backed_up" == "0" ]; then
   if [ "$app_md5" == "$c" ]; then
    already_backed_up=1
   fi
  else
   break
  fi
 done
}

# This function backs up the APK files, and calls the appropriate functions to handle both system and third-party apps
function apk_backup {
 for i in $paths_list; do
  case $i in
   /system/*)
    system_app_name_set $i
    ;;
   /data/app/*|/mnt/asec/*)
    aapt_output="$(aapt d badging "$i")"

    app_name_retrieve
    app_version_retrieve
    ;;
  esac

  if [ "$backup_directory_is_empty" == "0" ]; then
   md5_compare "$i"

   if [ "$already_backed_up" == "0" ]; then
    echo -ne "[ INFO ] Backing up "$displayed_name"..."

    case $i in
     /system/*)
      cp "$i" "$backup_location"/$app_name.apk
      ;;
     /data/app/*|/mnt/asec/*)
      cp "$i" "$backup_location"/$app_name"_"$app_version.apk
      ;;
    esac

    md5_array="$app_md5 $md5_array"

    echo " done."
   else
    echo "[ INFO ] "$displayed_name" has already been backed up."
   fi
  else
   echo -ne "[ INFO ] Backing up "$displayed_name"..."

   case $i in
    /system/*)
     cp "$i" "$backup_location"/$app_name.apk
     ;;
    /data/app/*|/mnt/asec/*)
     cp "$i" "$backup_location"/$app_name"_"$app_version.apk
     ;;
   esac

   echo " done."
  fi
 done

 if [ "$md5_array" != "$null" ]; then
  echo -ne "md5_array=\""$md5_array"\"" > "$script_location/md5_dictionary.txt"
 fi
}

# This function informs about the time that has been required by all of the operations
function goodbye {
 echo "[ INFO ] Operations took "$(((SECONDS/60)/60))" hours, "$(((SECONDS/60)%60))" minutes and "$((SECONDS%60))" seconds."
 echo "[ INFO ] All done!"
}

####################

# This is the core of the tool
echo "**************************"
echo " NEMRIS - App backup tool "
echo "  by Death Mask Salesman  "
echo "**************************"
echo ""

# Logic that handles the optional second argument that may be supplied by an user
# Only the "reset" argument is detected
if [ "$2" == "reset" ]; then
 settings_md5_files_delete
else
 settings_file_check
fi

packages_file_write

if [ "$is_aapt_missing" == "$null" ]; then
 aapt_check
fi

# Logic that aborts the execution if aapt is not present
if [ "$is_aapt_missing" == "1" ]; then
 echo "[ FATAL ] aapt is not installed: aborting."
 exit
fi

# Logic that decides whether variables to check
if [ "$backup_location" == "$null" ]; then
 if [ "$app_typology" == "$null" ]; then
  variables_default 0
 else
  variables_default 1
  app_typology_sanitize
 fi
else
 if [ "$app_typology" == "$null" ]; then
  variables_default 2
  else
   app_typology_sanitize
 fi
fi

backup_location_check

# Logic that checks whether to compute the checksums of the already backed up apps
if [ "$backup_directory_is_empty" == "0" ]; then
 if [ ! -e "$script_location/md5_dictionary.txt" ]; then
  md5_dict_generate
 else
  source "$script_location/md5_dictionary.txt"
 fi
fi

apps_list_retrieve

apks_paths_retrieve

echo ""

apk_backup

echo ""

goodbye

androidalle.com

AndroidAlle est une communauté de androiders où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X