Automatic cloud-free Android backups without root
So on every other device you can just set up rsync
or rsnapshot
, but android is developed by a bunch of corporate-bastards. So here we are with a 1500 word guide on how to give the OEMs the middle finger with their proprietary, partial and/or paid backup crap.
There are multiple potential ways of going about this all with pros and cons, depending on what you want. This will mostly focus on the methods I use to end up with the setup I’m currently running and touch on alternatives, marked with [alt.], I also explored.
Prerequisites
-
an Android phone with debugging in the developer-options enabled
-
Some sort of linux server to back up to with a usb-port accessible to plug in the phone. I’m using a raspi 4B.
Failing that you can also set
adb
up via wifi (or even ethernet). You can also have it controlled by the phone itself, but I have trust issues with android’s powersave. If you want to go that route look into Automate or Syncthing. -
unless you want to just use my setup, a bit of bash knowledge
Getting the data from Android
Before you create your own script, run adb devices
to get your ANDROID_SERIAL
number.
Get the app data via adb backup
This will open a dialogue on your phone that you have to confirm, so this is most useful with the “on plug in” running (see next point on how to bypass this).
Also be warned that adb restore
is not the most reliable utility, sometimes getting stuck on apps or just crashing for no good reason. But you can just extract the data from the backup (see below).
success=1
adb -s ${ANDROID_SERIAL} backup -all -keyvalue -f "${BACKUP_DEST}/backup.ab.tmp" || success=0
# adb just writes a very small file when you click "abort"
[[ $(find "${BACKUP_DEST}/backup.ab.tmp" -size +1M) == "" ]] && success=0
[alt.] unattended adb backup
I’ll just show some tricks to get adb backup
to run unattended, you’ll have to combine as needed whatever works for you. Just keep in mind it has to work with locked and off screen.
-
start by running
adb backup --all
and see what happens, if you’re really lucky this just works and you can just go to the automation section. -
Failing that try to run
adb root
before the backup (this usually requires a rooted phone, but supposedly it works without on some roms). - The last resort is
adb shell input
. This might require you go into the developer options of your phone and enabling “simulated input” or similar somewhere. If you’re lucky you can simulate all inputs needed to- turn on the screen
- unlock the phone
- click “backup” on the backup screen
- lock the phone again
But BE WARNED: anyone coming by during this will be able to mess with your phone, as the phone is unlocked for a bit.
- If you’re reading this, I’m sorry. I don’t know any other tricks. At some later point I figured out that on my ROM
adb shell input
requires root otherwise you just get some java exception. And when you have a rooted phone there are better ways entirely.
Get the media
For the data on the (internal-) sdcard there are two options:
adb pull
all the media
The internal data is usually stored in /storage/emulated/0/
, the external in /sdcard/
. We can just pull these folders. Note that this takes quite a while, because all media has to be copied.
# Backup the internal storage
mkdir "${BACKUP_DEST}/shared.tmp"
adb -s ${ANDROID_SERIAL} pull -a /storage/emulated/0/ "${BACKUP_DEST}/shared.tmp" > /dev/null || success=0
# Backup the sdcard
mkdir "${BACKUP_DEST}/sdcard.tmp"
adb -s ${ANDROID_SERIAL} pull -a /sdcard "${BACKUP_DEST}/backup/sdcard.tmp" > /dev/null || success=0
# check if device is still connected, otherwise backup probably failed
adb -s "${ANDROID_SERIAL}" get-serialno || success=0
[alt.] rsync
from the phone as media device
WARNING: What follows creates a giant security hole, look up Juice Jacking.
First configure your phone to appear as media device for data transfer when plugged into a computer by default (!). This can be done in the Developer-Options under “Standard USB Configuration”.
Afterwards copying the data is as simple as mounting the storage and copying the files. Here rsync
can be used to significantly speed up the operations by only copying changes, not all files like adb pull
.
# create mountpoint
mkdir /mnt/phone
# mount phone (optionally give -d to specify which phone)
jmtpfs /mnt/phone || success=0
# rsync for the internal storage
rsync -az "/mnt/phone/internal shared storage/" "${BACKUP_DEST}/backup/shared" || success=0
# rsync for the sd-card
rsync -az "/mnt/phone/external storage/" "${BACKUP_DEST}/backup/sdcard" || success=0
# check if device is still connected, otherwise backup probably failed
adb -s "${ANDROID_SERIAL}" get-serialno || success=0
When to backup
Unless you managed to make it past the adb backup
dialog a fully automated backup is sadly not possible. The next best thing I could think of is backing up whenever I charge my phone, that is plug it into said exposed USB port.
Whenever you plug the phone in
For this, plug the phone in and check lsusb
for the vendor and product id:
lsusb
# Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
# Bus 001 Device 022: ID 152d:0578 JMicron Technology Corp. / JMicron USA Technology Corp. JMS567 SATA 6Gb/s bridge
# Bus 001 Device 003: ID 04e8:3268 Samsung Electronics Co., Ltd ML-1610 Mono Laser Printer
# Bus 001 Device 024: ID 18d1:4ee7 Google Inc. #####################This one: Vendor 18d1, Product 4ee7
# Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
# Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
With these infos you can create a udev-rule that requests a systemd-service whenever you plug your phone in:
# /etc/udev/rules.d/80-phone-backup.rules
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="4ee7", TAG+="systemd", ENV{SYSTEMD_WANTS}="phone_backup.service"
And the corresponding service that runs the backup script
#/lib/systemd/system/phone_backup.service
[Unit]
Description=Start backup of Android Phone
[Service]
ExecStart=/etc/backup_phone.sh
Don’t forget to enable the service using
sudo systemctl daemon-reload
sudo systemctl enable phone_backup
Additionally I recommend prefixing your script with something like this find
trickery to prevent it from backing up too often:
do_backup=1
[[ $(find "${BACKUP_DEST}/backup.ab" -cmin -720) != "" ]] && do_backup=0
if [[ -n ((do_backup)) ]]
exit
fi
And in case you’re wondering why not to just use RUN+="/etc/backup_phone.sh"
in the udev rule: That will handily crash it, because udev runs those scripts on the main loop and therefore can’t handle any kind of hardware event while your backup is running.
[alt.] Cronjob
Open the crontab and enter a job running your script at some point at night, here I chose 2:05.
# crontab -e
5 2 * * * /etc/backup_phone.sh
(If this opens some sort of unresponsive text editor, it’s probably vim
. Press ESC
> :
> q
> !
> Enter
, then run export EDITOR=nano
and retry)
Telling the user what is going on
You can send notifications to the phone via adb
and I’m using a small wrapper to make that easier:
send_status() {
echo $1
adb -s ${ANDROID_SERIAL} shell "cmd notification post -t Backup backup '$1'"
}
Here the first Backup
is the title of the notification, the second is some kind of “group” the notification belongs to. Only the last Notification of some group is displayed.
Delta-compress the backups
In order to for any backup solution to be of some use, you want multiple snapshots of your data at different points in time. And in order to ba able to store them on an affordable harddrive, we can use delta compression. Essentially this is just keeping some initial version of the data and all changes to it.
For that we first need the files themself. Use abe to decompress the app-backups (the media data is already just files). Just drop the .jar
file in /opt
. Also it is recommended to install oracle’s java
not openjre, as the compression used by adb backup
is some java standard library and apparently reimplementations are prone to errors.
java -jar /opt/abe.jar unpack "${BACKUP_DEST}/backup.ab.tmp" - | tar -xC "${BACKUP_DEST}/backup"
Then use rsnapshot
to snapshot the folder you store all the decompressed data in.
# in /etc/rsnapshot.conf
backup /data/phone_backup/backup
Putting it all together
Here are the all the files I use (apart from the line in my rsnapshot.conf
) containing a bunch of additional bash scripting to make everything more stable and convenient.
- /etc/backup_phone.sh
- /lib/systemd/system/phone_backup.service
- /etc/udev/rules.d/80-backup_phone.rules
And how the files and folders it creates look like:
${BACKUP_DEST}/
├── backup The decompressed data
│ ├── date.txt The date the backup was run at
│ ├── apps/
│ ├── shared/
│ └── sdcard/
├── backup.ab The most recent adb backup output
├── backup.ab.tmp The currently loading adb backup out
├── shared.tmp/ The currently loading adb pull out
└── sdcard.tmp/ The currently loading adb pull out
Known Issues
Debug authorization is lost
This happens from time to time, it seems the keys expire sometimes after a reboot.
adb backup
fails every time
Try to add a bit of waiting beforehand. Android doesn’t like beeing commanded too quickly. Also click on the Backup button on your phone quite fast, sometimes the connection just dies.
Comments
As stated on the homepage, still under construction. Send me an email if you have questions and maybe I'll add it here manually