Use ADB to backup and restore local data for testing 2

Three years ago, I wrote a note about how to use adb to backup the test data. At that time, or at the time when I created the scripts (before writing the note), we were still able to save files in the common spaces such as SDCard by using

adb shell "run-as ..."

However, with the launch of Android 10, this is no longer possible. The command under run-as only able to save file within the app’s directory.

Permission denied

I was amazed by how Android Studio was able to implement the Save and Upload functions within the Device Explorer tool. Despite my extensive searching, I couldn’t find any clues on how the IDE implemented those features.

I had almost given up on my search until recently when I had to figure out how to do this again due to a bug related to the Release Candidate build. The RC build is signed with a different keystore from the development build, so testing the RC build required removing the app or cleaning up the previous backups. I used the Device Explorer to backup the backup files to my PC, but it became a burden when I needed to switch back and forth between the RC and development builds. This constant switching drove me crazy.

I needed to update my scripts to be able to save files in common spaces or on the computer instead of the app’s folder.

I searched again and came across a helpful gist: adb pull from app data directory without root access . Although I had doubts about the approach in the gist, due to the bug, I decided to keep it open until I felt better and could give it a try. As I suspected, the solution didn’t work as expected when I tried running the command:

run-as com.package.name cp /data/user/0/com.package.name/file.ext /sdcard

It’s not possible to bypass the permission to copy file from the app’s folder to the common space. However, I learned that the app’s folder is actually located under /data/user/0, not under /data/data as displayed in Android Studio.

I scrolled down to check the comments and felt lucky because I had kept the gist opened for a few days. A day after the day I first saw the gist, @listvin wrote a comment that provided a solution I had been searching for over 3 years:

adb shell "run-as $package base64 '$1'" | base64 -d > "$save_as"

This beautiful command gave me an idea of how to read a file and send it to another environment using Unix’s pipe:

First, read the file with adb shell run-as and send the content to the pipe. Then, the subsequent command receives data from the pipe and writes it to a new file.

Since my knowledge about bash is always poor, I applied the same base64 command to sync my backups on the device to the computer:

adb shell "run-as $package tar -czf file.gz -C path ."
adb shell "run-as $package base64 file.gz" | base64 -d > file.gz

Although base64 is slow, this snippet works. However, when syncing the file back to the device:

base64 file.gz | adb shell "run-as $package base64 -d > file.gz"

I encountered an issue with the > operator

Permission denied

I need another command to write the file without using > . This requirement led me to discover a special command called dd.

Similarly to cp, by default dd makes a bit-to-bit copy of the file, but with lower-level I/O flow control features.

Using dd, I was able to successfully sync the backup file to the device with the following command:

dd if=file.gz | adb shell "run-as $package dd of=file.gz"
# unzip and apply

This method is much faster than using base64 when transferring data between the device and computer. Typically, it takes only around 1 second to complete.

After 3 years of searching, I finally gained an understanding of how Android Studio implemented the Save and Upload features.


Bonus: Push and Pull files between the app’s folder and the computer

# Push
dd if="$1" | adb shell "run-as $package dd of='$2'"

# Pull
adb shell "run-as $package dd if='$1'" | dd of="$2"