When Android backup and restore tools may fail

  |   Source

The original question was originally posted on August 27, 2016 at Android StackExchange

Recently I've faced with a problem of migrating applications and their data from one device to another. Backing up and restoring is not a hard think, at least it should not be. However, standard Android approach may not work as expected causing weird issues. Let's take a look at one of them.

I have two devices that have Android 5.0.2 and 6.0.1 respectively (LG Optimus E975, Android 5.0.2, CyanogenMod 12-2015... and Samsung Galaxy S5, Android 6.0.2, flashed to a newer Sprint stock firmware (initially Android 5.0.x earlier)). I would like to migrate some non-cloud apps from the device with the older Android version to the new one using adb. The backing up command is simple:

adb backup -apk -f foo.bar.baz.ab foo.bar.baz

Now, after connecting the device with the newer Android version, the following command is expected to work:

adb restore foo.bar.baz.ab

Unfortunately it isn't. As usual, the restoring process fails silently just reporting that the restoring process is finished. Actually nothing really happens. And here are the logs:

adb logcat -s BackupManagerService
08-27 01:02:44.100  1300  2300 I BackupManagerService: Beginning full restore...  
08-27 01:02:44.100  1300  2300 D BackupManagerService: Starting restore confirmation UI, token=1956031088  
08-27 01:02:44.120  1300  2300 D BackupManagerService: Waiting for full restore completion...  
08-27 01:02:45.650  1300  2450 D BackupManagerService: acknowledgeFullBackupOrRestore : token=1956031088 allow=true  
08-27 01:02:45.660  1300 18181 I BackupManagerService: --- Performing full-dataset restore ---  
08-27 01:02:45.680  1300 18181 I BackupManagerService: Package foo.bar.baz not installed; requiring apk in dataset  
08-27 01:02:45.680  1300 18181 D BackupManagerService: APK file; installing  
08-27 01:02:45.680  1300 18181 D BackupManagerService: Installing from backup: foo.bar.baz  
08-27 01:02:45.690  1300 18181 E BackupManagerService: Unable to transcribe restored apk for install  
08-27 01:02:45.690  1300 18181 E BackupManagerService: Parse error in header: Invalid number in header: 'я╛В' for radix 8  
08-27 01:02:45.710  1300 18181 W BackupManagerService: io exception on restore socket read  
08-27 01:02:45.710  1300 18181 W BackupManagerService: java.io.IOException: Invalid number in header: 'я╛В' for radix 8  
08-27 01:02:45.710  1300 18181 W BackupManagerService:  at com.android.server.backup.BackupManagerService$PerformAdbRestoreTask.extractRadix(BackupManagerService.java:7380)  
08-27 01:02:45.710  1300 18181 W BackupManagerService:  at com.android.server.backup.BackupManagerService$PerformAdbRestoreTask.readTarHeaders(BackupManagerService.java:7179)  
08-27 01:02:45.710  1300 18181 W BackupManagerService:  at com.android.server.backup.BackupManagerService$PerformAdbRestoreTask.restoreOneFile(BackupManagerService.java:6396)  
08-27 01:02:45.710  1300 18181 W BackupManagerService:  at com.android.server.backup.BackupManagerService$PerformAdbRestoreTask.run(BackupManagerService.java:6254)  
08-27 01:02:45.710  1300 18181 W BackupManagerService:  at java.lang.Thread.run(Thread.java:818)  
08-27 01:02:45.710  1300  2300 I BackupManagerService: Full restore processing complete.  
08-27 01:02:45.720  1300 18181 D BackupManagerService: Full restore pass complete.  

It looks somewhat strange that the same backup format cannot be restored on a newer device. I've also tried to repack the underlying tarball using nelenkov/android-backup-extractor preserving the same file order (tar cvf ... -T files.lst) hoping that the broken header gets repaired (a precompiled binary, Git commit hash fcb4ee5af3ec973a406709de889a5cf340b61ce1, still can be downloaded here). No luck.

Trying out the backup and restore tools from Google Play didn't have a fine result as well. Some of the tools were just weird, and some of them, like Titanium Backup, were unable to restore the backups.

After a whole day of various experiments with another tools I've accidentally figured out that specifying the -apk key was the problem. Simply omitting the flag during a backup operation makes it fully restorable.

So my new backup/restore plan became as follows:

  • device 1: backup data only
  • device 2: backup APK+data retaining the APK only later
  • device 2: restore the APK using adb install
  • device 2: restore the data using adb restore

Here are the scripts:

backup.sh
#!/bin/bash

set -e

PACKAGE=$1

if [ -z "$PACKAGE" ]; then
    echo no package specified
    exit
fi

echo ">>> Backing up $PACKAGE data..."
adb backup -f "$PACKAGE".DATA.ab $PACKAGE

echo ">>> Backing up $PACKAGE APK..."
adb backup -apk -f "$PACKAGE".APK.ab $PACKAGE

echo ">>> Extracting APK..."
java -jar abe-all.jar unpack "$PACKAGE".APK.ab "$PACKAGE".APK.ab.tar
rm "$PACKAGE".APK.ab
APK_FILENAME=$(basename $(tar tf "$PACKAGE".APK.ab.tar | grep '^apps/'"$PACKAGE"'/a/'))
tar xvf "$PACKAGE".APK.ab.tar -C ./ "apps/$PACKAGE/a/$APK_FILENAME" --strip-components=3
mv "$APK_FILENAME" "$PACKAGE".apk
rm "$PACKAGE".APK.ab.tar

echo
echo "*************"
echo "*** Done ***"
echo "*************"

This script requires a package name as the input parameter and stores an APK and its data as foo.bar.apk and foo.bar.DATA.ab respectively. I also implemented it as a twice-backup script (a device asks for making a backup two times) because I didn't want to re-pack underlying tarballs preserving the original file order just making sure that the DATA.ab file is in its original state.

restore.sh
#!/bin/bash

set -e

PACKAGE=$1

if [ -z "$PACKAGE" ]; then
    echo no package specified
    exit
fi

echo ">>> Installing $PACKAGE APK..."
adb install -r $PACKAGE.apk

echo ">>> Restoring $PACKAGE data..."
adb restore $PACKAGE.DATA.ab

echo
echo "************"
echo "*** Done ***"
echo "************"

This script simply installs an APK on a target device and restores its data. The only input parameter is the application package name.

So the overall backup/restore process is:

  • Connect device #1
  • Execute backup.sh passing an application package name
  • Click "Backup..." on the device #1 (one will be asked two times for the data and APK+data backups respectively)
  • Repeat the previous two steps for other packages if necessary
  • Disconnect the device #1 and connect the device #2
  • Execute restore.sh passing an application package name
  • Click "Restore..." on the device #2 once asked
  • Repeat the previous two steps for other packages if necessary

That's all. Extracting the APK files also lets me keep applications that had been removed from Google Play.

Comments powered by Disqus