Univers Libre

Automatically backup your Android phone with udev

Written on 1 March 2019, 18:20 CET
Tags: tech.

I wrote a simple script and udev rule a few years ago to automatically backup my phone (running Replicant OS, a free, FSF-approved Android distribution) to my computer when I plug it to a USB port. I just published those scripts after some clean-up on a git repository and wrote instructions on how to set it up.

I know there are plenty of Android applications to backup my data and upload them somewhere via Internet. But my phone is rarely connected, the Wi-Fi is usually turned off and I have no data plan. Instead I do plug my phone to my computer on a regular basis to recharge it, so it seemed obvious to automatically trigger a backup of my phone via USB.

udev can be used to detect events, like plugging-in a device, and trigger actions based on these events. In my case, the udev rule looks like this:

ACTION=="add", SUBSYSTEMS=="usb", ENV{ID_SERIAL_SHORT}=="XXXXXXXXXXXXXXXX", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}+="android-backup@$env{ID_SERIAL_SHORT}.service"

Where ID_SERIAL_SHORT is used to uniquely identify my phone. The rule triggers the android-backup@.service systemd unit (in user mode, no need to run it as root).

The systemd unit is very simple, there is no need to specify an [Install] section, targets or anything like that since it is triggered directly by udev:

[Service]
Type=oneshot
ExecStart=%h/.local/bin/android-backup.sh %I

So the unit simply calls the following shell script:

#!/bin/sh

export DISPLAY=0:0
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
BACKUP_DIR=~/replicant-backup/
RSYNC_EXCLUDE=~/.config/android-backup.exclude

notify-send "Android Backup" "Starting Android backup"

(
set -e

mkdir -p "$BACKUP_DIR"
adb wait-for-device 2>&1 |(grep -Ev "^(\* daemon not running; starting now at|* daemon started successfully$)" || true)
adb root
adb wait-for-device
adb shell rsync --daemon --no-detach --config=/system/etc/rsyncd.conf &
adb forward tcp:6010 tcp:873
sleep 2
rsync -av --chmod u+rwX --delete --delete-excluded --exclude-from "$RSYNC_EXCLUDE" rsync://localhost:6010/root/ $BACKUP_DIR
adb forward --remove tcp:6010
adb shell pkill rsync
)

if [ $? -ne 0 ]; then
    notify-send "Android Backup" "Backup terminated with errors. See journalctl --user -eu android-backup for details"
else
    notify-send "Android Backup" "Backup terminated successfully"

In this script, I first start adb (as root so it can have access to the whole system) and start a rsync daemon on the OS running on the phone and forward its TCP port through adb. I can then start a normal rsync from my computer to fetch all the files and directories. The destination directory (on my computer) is included in my daily backup, thus I automatically have incremental copies of my phone's data.