Copy Android application folder with adb
While I do some development on Android, I’m no expert in it, and thus I’m surely missing some useful tools for simplifying my life.
One thing that confuses me constantly is how inconvenient I find it to develop and debug on a real device.
If you want to do something outside the IDE, the tooling seems to be very scarce.
A limitation I run into quite often is how to analyze files that have been saved in the application directory.
At least from Android Studio, you can click through the path, then click on the file you want to analyze and copy it on your PC.
The main disadvantage of this approach, not to mention that I do not like the IDE, is that you have to click through the path every time.
If you analyze, for example, a log file, do some modifications, and restart the application, Android Studio does not remember the last location you were, and you have to click your way through the correct file again and again.
Note 📝 | Yes, I would love to have ssh acces, and eventually install some other utilities, just like on all other machines. I have ssh installed on Termux, but it is not able to access the application folder of other applications; you must use adb , or install ssh system-wide, which is not possible without administrator rights, or a custom rom. |
Copy files with adb pull
and adb push
It is possible to copy files from and to the device with adb pull
and adb push
:
# copy file from device to machine
adb pull "$PATH_ON_DEVICE" "$PATH_ON_PC";
# copy file from machine to device
adb push "$PATH_ON_PC" "$PATH_ON_DEVICE";
But it has two big limitations:
-
it works only for files, not for folders
-
it does not work if you want files to copy to or from the application folder
Since I wanted to copy the whole application folder from the device to my machine, adb pull
does not seem to be the right tool.
adb shell run-as
To access files in the application folder, you need to use run-as
:
adb shell run-as "$APP_ID" ls "/data/data/$APP_ID/files";
Unfortunately when using adb shell run-as
, there is no way to transfer files over the adb protocol 😠.
Another thing to pay attention to is that the application needs to be built as debuggable 🗄️, if not you will not be able to access the application folder unless you have administrative rights, or some other device-specific methods available.
Copy files with cat
The "official" way (as in, I found no better way from the command line) is to use cat
, and redirect the output on the developer machine to a file.
adb shell run-as "$APP_ID" cat "/data/data/$APP_ID/files/$FILE_I_WANT_TO_INSPECT" > "$FILE_I_WANT_TO_INSPECT";
It works (at least on Unix-like shells), but it is not as robust as one expects.
In particular, the file "$FILE_I_WANT_TO_INSPECT"
is created on my machine before the adb
command is executed, thus if adb shell run-as
fails for some reason, there is an empty file and an error on the console. Or (worst) there is no error in the console, but the error has been saved to the file.
The second issue is that this approach only works with single files. Copying all files, using ls
and cat
recursively, is doable, but too error-prone.
Copy files with tar
At least there is a stripped-down version of tar
, thus with something like
adb exec-out run-as "$APP_ID" tar -c -C /data/data/ "$APP_ID" > "$APP_ID.tar";
I can copy all files in one go.
Unfortunately, from time to time, this command failed, or the archive was missing some data.
The root cause was that the application I was working with opened some sockets in the application folder files/sockets
and tar
was not able to archive them.
Once I found out the root cause, it was possible to change the tar command to the following:
adb exec-out run-as "$APP_ID" tar -c --exclude "$APP_ID/files/sockets" -C /data/data/ "$APP_ID" > "$APP_ID.tar";
Since then, my archives have always been error-free.
Another useful improvement was to automatically attach to the tar archive the output of logcat
adb exec-out run-as "$APP_ID" tar -c --exclude "$APP_ID/files/sockets" -C /data/data/ "$APP_ID" > "$APP_ID.tar";
timeout 5s adb logcat > /tmp/logcat.txt;
tar --append --file="$APP_ID.tar" -C /tmp logcat.txt;
Note that adb logcat
accepts as a parameter a timestamp or the number of lines it should output. It is possible, for example, to query the output of the last two minutes:
adb logcat -t "$(date +'%m-%d %H:%M:%S.000' --date='2 minutes ago')";
But in practice, I often do not know how much data I’ll need, and my experience until now has shown that waiting 5 seconds gives me more than enough data to analyze.
While this methods works well enough, it is not as robust as I would like. There is always the possibility that the tar
command produces an invalid archive, like it did before I excluded the socket files, and it seems that tar
on Android prints the error messages to stdout and not stderr.
A possible variation of my current approach is to extract the data automatically.
adb exec-out run-as "$APP_ID" tar -c --exclude "$APP_ID/files/sockets" -C /data/data/ "$APP_ID" | tar xf - -C /work/folder;
timeout 5s adb logcat > /work/folder/logcat.txt
But I prefer to do the extraction separately, especially since the tar archive might not be valid, and I might need to inspect the "raw" output.
For my workflow, working with the archive itself is practical enough, especially since I share those archives with other people.
Do you want to share your opinion? Or is there an error, some parts that are not clear enough?
You can contact me anytime.