#!/bin/sh
# adb_backup.sh
# 2026-03-12
# by Gernot Walzl
# Backup and restore data of Android apps.
# ADB root debugging is required. Tested on Android 15.
set -e
CWD=$(pwd)
TMP=${TMP:-'/var/tmp'}
USER_ID=${USER_ID:-'0'}
print_usage () {
echo "Usage: $0 {list|backup PACKAGE|backup-all|restore FILENAME}"
echo "Example: $0 backup org.videolan.vlc"
echo "Example: $0 restore org.videolan.vlc_FP5_2026-01-31.zip"
}
list () {
adb shell "pm list package -3 | sed 's/^package://' | sort"
}
backup () {
PKG="$1"
if [ "$(adb shell whoami)" != "root" ]; then
adb root
fi
adb shell "test -d '/data/user/$USER_ID/$PKG'" || return 1
adb shell "am stop-app '$PKG'"
echo
echo "Backing up $PKG:"
mkdir -p "$TMP/$PKG"
cd "$TMP/$PKG" || return 1
echo "/data/user/$USER_ID/$PKG"
adb exec-out "tar -cz -C '/data/user/$USER_ID' '$PKG' 2> /dev/null" \
> "${PKG}_data.tar.gz"
adb shell "cd '/data/user/$USER_ID' \
&& find '$PKG' -type f -exec sha256sum '{}' \;" \
> "${PKG}_data.sha256"
DIRS=$(adb shell "cd '/storage/emulated/$USER_ID' \
&& find Android -type d -name '$PKG'")
for DIR in $DIRS; do
echo "/storage/emulated/$USER_ID/$DIR"
ARCHNAME="${PKG}_$(dirname "$DIR" | tr / _)"
adb exec-out "tar -cz -C '/storage/emulated/$USER_ID' '$DIR' 2> /dev/null" \
> "${ARCHNAME}.tar.gz"
adb shell "cd '/storage/emulated/$USER_ID' \
&& find '$DIR' -type f -exec sha256sum '{}' \;" \
> "${ARCHNAME}.sha256"
done
PKGPATHS=$(adb shell "pm path '$PKG' | sed 's/^package://'")
for PKGPATH in $PKGPATHS; do
echo "$PKGPATH"
adb pull "$PKGPATH" \
> /dev/null
BASENAME=$(basename "$PKGPATH")
mv "$BASENAME" "${PKG}_${BASENAME}"
done
REMOTEHOST=$(adb shell "echo \$HOSTNAME")
ZIPFILE="${PKG}_${REMOTEHOST}_$(date +%Y-%m-%d).zip"
echo
echo "Creating $ZIPFILE:"
zip -r "$CWD/$ZIPFILE" .
cd "$CWD"
rm -rf "$TMP/$PKG"
}
backup_all () {
PKGS=$(list)
for PKG in $PKGS; do
backup "$PKG"
done
}
restore () {
FILENAME=$(realpath "$1")
if [ "$(adb shell whoami)" != "root" ]; then
adb root
fi
PKG=$(unzip -l "$FILENAME" \
| sed -n '/_Android_/d;s/.* \(.*\)_data.tar.gz$/\1/p')
if [ -z "$PKG" ]; then
return 1
fi
adb shell "am stop-app '$PKG'"
echo
mkdir -p "$TMP/$PKG"
cd "$TMP/$PKG" || return 1
unzip "$FILENAME"
echo
echo "Restoring $1:"
for APK in *.apk; do
echo "$APK"
adb install --user "$USER_ID" "$APK"
done
USER_APP=$(adb shell "stat -c '%U' '/data/user/$USER_ID/$PKG'")
for ARCHIVE in *_Android_*.tar.gz; do
echo "$ARCHIVE"
cat "$ARCHIVE" \
| adb shell "tar -xzf - -C '/storage/emulated/$USER_ID'"
done
adb shell "cd '/storage/emulated/$USER_ID' \
&& find Android -type d -name '$PKG' -exec chown -R '$USER_APP' '{}' \;"
echo "${PKG}_data.tar.gz"
cat "${PKG}_data.tar.gz" \
| adb shell "tar -xzf - -C '/data/user/$USER_ID'"
adb shell "chown -R '${USER_APP}:${USER_APP}' \
'/data/user/$USER_ID/$PKG'"
adb shell "chown -R '${USER_APP}:${USER_APP}_cache' \
'/data/user/$USER_ID/$PKG/cache' \
'/data/user/$USER_ID/$PKG/code_cache'"
adb shell "restorecon -DR '/data/user/$USER_ID/$PKG'"
echo
echo "Checking restored data:"
for CHECKSUMS in *_Android_*.sha256; do
echo "$CHECKSUMS"
if [ -s "$CHECKSUMS" ]; then
cat "$CHECKSUMS" \
| adb shell "cd '/storage/emulated/$USER_ID' && sha256sum -cs -" \
|| echo "ERROR: 'sha256sum -c $CHECKSUMS' failed!"
fi
done
echo "${PKG}_data.sha256"
cat "${PKG}_data.sha256" \
| adb shell "cd '/data/user/$USER_ID' && sha256sum -cs -" \
|| echo "ERROR: 'sha256sum -c ${PKG}_data.sha256' failed!"
cd "$CWD"
rm -rf "$TMP/$PKG"
}
case "$1" in
'list')
list
;;
'backup')
backup "$2"
;;
'backup-all')
backup_all
;;
'restore')
restore "$2"
;;
*)
print_usage
esac