Working with adb aka Android Debug Bridge
By Martin 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:
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
ls:[...]
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
package:/data/app/com.microsoft.appmanager-1/base.apk=com.microsoft.appmanager
package:/data/app/com.microsoft.office.officelens-2/base.apk=com.microsoft.office.officelens
package:/data/app/com.microsoft.skydrive-1/base.apk=com.microsoft.skydrive
package:/data/app/com.microsoft.office.onenote-2/base.apk=com.microsoft.office.onenote
package:/data/app/com.microsoft.emmx-2/base.apk=com.microsoft.emmx
package:/data/app/com.microsoft.office.outlook-1/base.apk=com.microsoft.office.outlook
package:/data/app/com.microsoft.office.word-2/base.apk=com.microsoft.office.word
HWSLA-Q:/ $ pm list packages | grep qr
package:com.kitkats.qrscanner
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]
[ro.build.version.all_codenames]: [REL]
[ro.build.version.base_os]: []
[ro.build.version.codename]: [REL]
[ro.build.version.incremental]: [BB_R2.03]
[ro.build.version.preview_sdk]: [0]
[ro.build.version.release]: [6.0.1]
[ro.build.version.sdk]: [23]
[ro.com.google.gmsversion]: [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.ms (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=http://schemas.android.com/apk/res/android (line=2)
E: manifest (line=2)
A: http://schemas.android.com/apk/res/android:versionCode(0x0101021b)=19295
A: http://schemas.android.com/apk/res/android:versionName(0x0101021c)="4.252.0.469.19295" (Raw: "4.252.0.469.19295")
A: http://schemas.android.com/apk/res/android:installLocation(0x010102b7)=0
A: http://schemas.android.com/apk/res/android:compileSdkVersion(0x01010572)=29
A: http://schemas.android.com/apk/res/android:compileSdkVersionCodename(0x01010573)="10" (Raw: "10")
A: package="at.oebb.ts" (Raw: "at.oebb.ts")
A: platformBuildVersionCode=29
A: platformBuildVersionName=10
E: uses-sdk (line=8)
A: http://schemas.android.com/apk/res/android:minSdkVersion(0x0101020c)=21
A: http://schemas.android.com/apk/res/android:targetSdkVersion(0x01010270)=29
E: uses-permission (line=12)
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.
[...]
If your not investigating a business related bug but an app crash you
may be interested in the command adb bugreport
.
run-as
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.
Sample:
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”
logcat
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:
- on app start execute a process with
logcat -vtime -d *:W
with ProcessBuilder - this will fetch the latest Warning, Errors and Crashes
- forward the received logcat result to the app logger service (if no logcat result was fetched, there was no problem)
- 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
Propose a change