Working with adb aka Android Debug Bridge

on Regards: Android; Article; linux;

Android Debug Bridge

Working with the command-line tool adb can come in very handy, when you are trying to analyze an installed android app on your device. To get started you need install the Android SDK which ships the tool adb (along with some other helpful applications). I find it quite pleasant to add the directory, which is containing the executable adb, to the $PATH environment. So adb will always be available in the command-line (without switching into the android skd binary folder). More Fundamentals about adb see here

Getting started

The next step is to “activate” the adb endpoint on your (mobile) device. This can be achieved by enabling USB debugging under Developer options. Maybe as a side note, this works probably on most consumer mobile devices. If you own a work phone, its possible that the company applied restrictive policy rules and enabling ‘USB debugging’ is not enough to get the adb server working.

If you are activating USB debugging and connecting the device to your computer for the first time you should see something like this:

Abb. 1. enable developer Abb. 1. allow usb debugging Abb. 1. allow usb debugging fingerprint

In this case the device should be listed with the command adb devices. If your device isn’t listed by the command adb devices, you need to make sure that the connection from the computer to the device is working. You can try exchanging the USB-Cable or disable/enabling USB debugging, revoking all USB debugging authorizations etc.

adb devices
List of devices attached
AEUBB17928505230        device

Android is running some kind of linux derivation, and adb makes it accessible by the command adb shell. This way we are able to execute some known Linux commands like ls, ps and so on. You can do that by adb shell YOURCOMMAND or adb shell which opens the bash. Lets do so.

adb shell
1|HWSLA-Q:/ $ ls
ls: ./verity_key: Permission denied
acct       charger cust_comm data  dsp      hw_oem oem     product           root   storage tombstones
bugreports config  cust_spec dev   etc      log    persist property_contexts sbin   sys     vendor
cache      cust    d         dload firmware mnt    proc    res               sdcard system  version
1|HWSLA-Q:/ $

You see the typical Filesystem Hierarchy Standard of linux. But lets move on and display all installed app packages on the device. We can use the command pm (or adb shell cmd package list packages but then you have to exit bash) with the parameter -f (to display the installed path) and use grep to filter for a specific app name. For example:

HWSLA-Q:/ $ pm list packages -f | grep microsoft
HWSLA-Q:/ $ pm list packages | grep qr

To display device related settings like the Android OS version you can use the following command:

adb shell "getprop | grep version"
[bluebird.mdm.sdkversion]: [23.3.0]
[gsm.version.rilimpl]: [Qualcomm RIL 1.0]
[hw.cabl.version]: [2.0.20140905]
[]: [REL]
[]: []
[]: [REL]
[]: [BB_R2.03]
[]: [0]
[]: [6.0.1]
[]: [23]
[]: [6.0_r11]
[ro.opengles.version]: [196608]

Investigate bugs in your app

Sometimes it’s hard to reproduce bugs from a production environment. In this case it’s helpfull to know the app state at least when the issue occurred. For example, how did the sql lite database look like in your app or which settings were used. In this case we can try to obtain the whole app data by creating a backup of the app.

To create a backup of an android app you can use adb backup. Make sure you exited the linux device bash with exit. In the sample I created a backup of the app package at.oebb.ts and stored the backup into the file oebb.backu (If you’re wondering backu is just a typo I was too lazy to fix). When calling the command you need to unlock your device and press “Backup my Data” (the password field can be empty).

C:\Users>adb backup -apk -shared packages at.oebb.ts -f oebb.backu
Now unlock your device and confirm the backup operation...  

Lets extract the backup so we are able to see the content of it. For this I use ( printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" ; tail -c +25 oebb.backu ) | tar xfvz - with WSL (Windows-Subsystem for Linux).

From the extracted files you can determine that the app “öbb” is using a sqlite database (Application.db for storing data) and for logging and app usage analysis they use the service of Microsoft named (AppCenter.xml).

There is also another way to retrieve the data from the device to your computer. Lets use adb pull for it. First we copy our files to the /sdcard/ (adb pull has not always access to the directory /sdcard) by using adb shell "cp /data/app/at.oebb.ts-2/base.apk /sdcard/base.apk". Now we can pull it from our device as shown below.

adb pull /sdcard/base.apk base.apk.oebb
/sdcard/base.apk: 1 file pulled. 21.8 MB/s (23672488 bytes in 1.034s)

Having the base.apk allows us to view the AndroidManifest.xml with aapt2 d xmltree --file AndroidManifest.xml base.apk.oebb (The program aapt2 is also shipped with the Android SDK.). This shows the version, the permission and some other details the app is using.

C:\Users>aapt2 d xmltree --file AndroidManifest.xml base.apk.oebb
N: android= (line=2)
  E: manifest (line=2)
    A:"" (Raw: "")
    A:"10" (Raw: "10")
    A: package="at.oebb.ts" (Raw: "at.oebb.ts")
    A: platformBuildVersionCode=29
    A: platformBuildVersionName=10
      E: uses-sdk (line=8)
      E: uses-permission (line=12)

If your not investigating a business related bug but an app crash you may be interested in the command adb bugreport.


To run shell commands in the context of your app you can use the command run-as. An example command would be adb shell run-as apppackagename yourcommand, this only works if a debugable version of the app is installed (which is mainly the case if you are the developer of the app). You may ask, why would I like to fire shell commands from within my app?

Imagine you received a sqlite database from a productive device and you want to reproduce an issue which was reported with it. Because normally you don’t have enough privileges to override the sqlite database within your app data folder, you can do this when using run-as

To push the file (e.g. the sqlite) from your computer to the (mobile) device use adb push local filesystem /sdcard/filename. After the file is pushed to the sdcard of the device make use of adb shell in combination with cp or mv to move it further to the app location. If there is already a file existing on the device with the same name in your target location, I would recommend to rename it as cp -f didn’t work out for me.


rem first remove the file (better rename it)
adb -d shell "run-as at.oebb.ts rm /data/data/at.oebb/db/Application.db"
rem copy db to the sdcard
adb push Application.db /sdcard/Application.db
rem copy from sdcard to app folder
adb -d shell "run-as at.oebb.ts cp /sdcard/Application.db /data/data/at.oebb/db/Application.db -rf"

Note: If you don’t have the debugable app of at.oebb.ts, run-as will not work.

File Descriptors

File Descriptors issues are evil. Your app is allowed to use 1024 file descriptors (fd) and if your app uses more than the allowed, the app will get killed by the OS. For the user of your app it will look like the app crashed. So lets hunt down the root cause. You can view the current file descriptors by opening the virtual directory of:

shell@EF500:/ $ ps | grep {appname}
u0_a520   {pid}  354   1167264 101824            0000000000 R at.oebb.ts
shell@EF500:/ $ ls /proc/{pid}/fd/ -la
opendir failed, Permission denied

Hmm. Lets run the same command again but this time with run-as!

C:\Users>adb shell run-as {appname} ls /proc/{pid}/fd/ -la
lrwx------ u0_a520  u0_a520           2021-02-14 10:37 0 -> /dev/null
lrwx------ u0_a520  u0_a520           2021-02-14 10:37 1 -> /dev/null
lr-x------ u0_a520  u0_a520           2021-02-14 10:37 11 -> /dev/__properties__
l-wx------ u0_a520  u0_a520           2021-02-14 10:37 12 -> /dev/cpuctl/bg_non_interactive/tasks
lrwx------ u0_a520  u0_a520           2021-02-14 10:37 13 -> anon_inode:[eventfd]
lrwx------ u0_a520  u0_a520           2021-02-14 10:37 14 -> socket:[30898]
lrwx------ u0_a520  u0_a520           2021-02-14 10:37 15 -> socket:[29279]
lr-x------ u0_a520  u0_a520           2021-02-14 10:37 16 -> pipe:[29280]
l-wx------ u0_a520  u0_a520           2021-02-14 10:37 17 -> pipe:[29280]
lrwx------ u0_a520  u0_a520           2021-02-14 10:37 18 -> anon_inode:[eventpoll]
lr-x------ u0_a520  u0_a520           2021-02-14 10:37 19 -> /data/app/at.oebb/base.apk
lrwx------ u0_a520  u0_a520           2021-02-14 10:37 2 -> /dev/null
lr-x------ u0_a520  u0_a520           2021-02-14 10:37 20 -> /dev/urandom
lr-x------ u0_a520  u0_a520           2021-02-14 10:37 21 -> /system/usr/share/zoneinfo/tzdata
lrwx------ u0_a520  u0_a520           2021-02-14 10:37 22 -> /data/data/at.oebb/db/Application.db

That looks way better! To count all file descriptors use adb shell run-as {appname} "ls /proc/{pid}/fd/ -la | wc -c". Start your app and interact with it. At the same time try to document the amount of the file descriptors. This way you should be able to see if the amount of file descriptors is increasing over time when your app gets used. If available, you can use automated UI Testing for this scenario. Also you can try to log the file descriptor amounts of a certain group like (file fd, socket fd,…). This should help you to geht an idea where the problem is located. Find out more about the topic here “Android Memory and File Descriptor Leaks, Diagnosis and Debugging”


Another great command is logcat which can be used to retrieve logs. Depending on which context you are calling logcat you receive system wide logs or you only receive logs of your app.

From an app perspective we can try to use logcat to detect a possible app crash (Disclaimer: You won’t see if the OS killed your app). This could be implemented like this:

  1. on app start execute a process with logcat -vtime -d *:W with ProcessBuilder
  2. this will fetch the latest Warning, Errors and Crashes
  3. forward the received logcat result to the app logger service (if no logcat result was fetched, there was no problem)
  4. clear logcat with logcat -c

With the ProcessBuilder we can execute any command as we did with adb shell as long we have sufficient privileges. For example, you need permission to the Picture folder if you want to “read” the content of Pictures with ls and ProcessBuilder. Some commands or directories cannot be accessed as a “normal” developer and are restricted for firmware manufacturer. A sample implementation on how to read logcat from within an app can be seen here and here.

More about Android Log Analysis