Skip to content

Instantly share code, notes, and snippets.

@stdedos
Forked from bashenk/get_android_service_call_numbers.sh
Last active December 18, 2020 18:57
Show Gist options
  • Save stdedos/f0a5a43297b1926d5cb730fcef7ac125 to your computer and use it in GitHub Desktop.
Save stdedos/f0a5a43297b1926d5cb730fcef7ac125 to your computer and use it in GitHub Desktop.
POSIX compliant and portable fork, with ability to set the desired android version to check with the parameter -v

Calling Android services from ADB shell Many android automation recipes contain references to the "service call" command:

# service
Usage: service [-h|-?]
    service list
    service check SERVICE
    service call SERVICE CODE [i32 INT | s16 STR] ...
Options:
  i32: Write the integer INT into the send parcel.
  s16: Write the UTF-16 string STR into the send parcel.

The command is indeed very useful. But the problem is that the calling "CODES" are android version specific and recipes often get out dated sometimes even after a minor version update. I am frequently being asked to update those codes for my recipes. I decided to share a small bash script I have been using to look up service codes for specific Android versions.

This script checks the Android version of your phone and the java package name of the service you specified as a parameter. Then the script downloads the service AIDL file for your phone's Android version from https://android.googlesource.com and parses it. So obviously it only works for the standard Android services it can find the source code for.

This is how I usually run the script. For example let's find out the calling code for "getCallState()" method of the "phone" service. I usually have multiple phones connected to my system so I need to specify which one to use by setting the ANDROID_SERIAL variable first. If you have just a single phone connected you can skip that part:

~$ ANDROID_SERIAL=XXXXXXXXXX ./get_android_service_call_numbers.sh phone | grep getCallState
ANDROID_SERIAL=XXXXXXXXXX
ro.build.version.release=4.4.4
TAG=android-4.4.4_r1
SERVICE=phone
SERVICE_PACKAGE=com.android.internal.telephony.ITelephony
    32 int getCallState()
#!/bin/sh -eu
Exit () {
if [ $# -ge 1 ] && [ -n "$1" ] && [ "$1" -eq "$1" ] 2>/dev/null; then exitCode="$1"; shift; fi
if [ $# -ge 1 ] ; then printf '%s\n' "$@" 1>&2; fi
if (return 0 2>/dev/null); then # file was sourced rather than run
kill -INT 0 # Ensures exiting of the script, even from nested subshells
else
exit "${exitCode:-1}";
fi
}
unset adbSerial
unset androidVersion
unset debug
while getopts ":ds:v:" OPT; do
case "$OPT" in
d ) debug=true ;;
s ) adbSerial="$OPTARG" ;;
v ) androidVersion="$OPTARG" ;;
\? ) Exit 2 "Argument '-$OPTARG' not recognized" ;;
esac
done
shift $((OPTIND-1))
AssertExecutablesAreAvailable () {
bins=
for b in "$@"; do [ -n "$(command -v "$b" 2>/dev/null)" ] || bins="${bins-}$b "; done
if [ ${#bins} -eq 0 ]; then return 0; fi
printf '%s\n' "Cannot find the following executables:" "$bins"
printf 'Install them now? '
read -r REPLY
[ "$REPLY" != "${REPLY#[Yy]}" ] || Exit 1 "Exiting due to insufficient dependencies"
sudo apt install "$bins"
}
AssertExecutablesAreAvailable 'adb' 'wget' 'tr' 'sed' 'cut' 'grep' 'head' 'tail' 'base64' 'printf' 'cat' 'sort'
googleSource="https://android.googlesource.com"
repoPath="platform/frameworks/base"
if [ -n "${debug:+true}" ]; then
echo "Stopping pre-processing due to debug=$debug";
return 0 2>/dev/null || exit 0;
fi
if [ $# -eq 0 ]; then
Exit 2 "Please provide a service name argument"
else
serviceName="$1"
fi
# Use adbSerial variable for device, or grab the first available device's serial number
adbSerial="${adbSerial:-$(adb devices | sed -e '1d' -e '2s/[[:space:]].*//' -e 'q')}"
ADBShell () { adb ${adbSerial:+-s $adbSerial} shell -T -x "$1" | tr -d '\r'; }
GetAndroidVersion () {
ver="$(ADBShell 'getprop ro.build.version.release')";
echo "ro.build.version.release = $ver" 1>&2;
echo "$ver";
}
GetAndroidTagForVersion () {
version="${1-}";
[ -n "$version" ] || Exit 1 "Android version was not provided in GetAndroidTagForVersion";
allTags="$(wget -qO - "$googleSource/$repoPath/+refs/tags/?format=text" | sed -e '/\^{}/d; s/.*\///' -e 'h' -e '/android-[0-9]/!d' | sort -V )"
latestTags="$(echo "$allTags" | sed -e 'h' -e 'N' -e '/^\(android-[0-9.]\+[a-z]*_r\)\([.0-9a-z]*\)\n\1/D' -e 'x' )"
localTag="$(echo "$latestTags" | sed -e "/android-$(echo "$version" | sed -e 'y/././')/p;d" | tail -n 1)"
[ -n "$localTag" ] || Exit 1 "TAG not valid!" "List of valid tags:" "$allTags"
echo "$localTag"
}
GetServicePackageName () {
service="${1-}"
[ -n "$service" ] || Exit 1 "Service name was not provided in GetServicePackageName";
packageName=$(ADBShell 'service list' | sed -e '1d' -e 's/[0-9]*[[:space:]]'"$service"': \[\(.*\)\]/\1/p' -e 'd');
[ -n "$packageName" ] || Exit 1 "Unable to find service package name for '$service'";
echo "$packageName";
}
GetGoogleSourceFile () {
[ -n "$1" ] || Exit 1 "Tag was not provided in GetGoogleSourceFile";
[ -n "$2" ] || Exit 1 "File was not provided in GetGoogleSourceFile";
wget -qO - "$googleSource/$repoPath/+/$1/$2?format=text" | base64 -d
}
GetAllServicesForTag () {
version="${1-}"; localTag="${2-}";
[ -n "$version" ] || Exit 1 "Android version was not provided in GetAllServicesForTag";
baseVersion="${version%%.*}"
[ "$baseVersion" -eq "$baseVersion" ] 2>/dev/null || Exit 1 "Could not parse number from Android version in GetAllServicesForTag";
[ -n "$localTag" ] || Exit 1 "Tag was not provided in GetAllServicesForTag";
if [ "$baseVersion" -gt 8 ]; then file='Android.bp'; else file='Android.mk'; fi
GetGoogleSourceFile "$localTag" "$file" | tr -d ' \\\t,"' | sed -e '/\.aidl$/!d' -e '/^gen:/d' | sort -u
}
ParseServiceAIDL () {
localTag="${1-}"; servicesList="${2-}"; packageName="${3-}"
[ -n "$localTag" ] || Exit 1 "TAG was not provided in ParseServiceAIDL";
[ -n "$servicesList" ] || Exit 1 "Services list was not provided in ParseServiceAIDL";
[ -n "$packageName" ] || Exit 1 "Service package name was not provided in ParseServiceAIDL";
packageFilePath="$(echo "$packageName" | tr '.' '/')\.aidl"
servicePath="$(echo "$servicesList" | grep -m 1 "$packageFilePath\$")"
GetGoogleSourceFile "$localTag" "$servicePath" | sed -e '1,/interface/d' -e '/^$/d' -e 's/^[[:space:]]*//' -e '/^[^a-zA-Z]/d' -e '/^[^;]*$/{$!N}' -e '$d' -e 's/\(^\|\n\)[[:space:]]*\|\([[:space:]]\)\{2,\}/\2/g' | cat -n
}
echo "ADBSerial=$adbSerial"
echo "AndroidOSVersion=${androidVersion:="$(GetAndroidVersion)"}"
echo "VersionTag=${tag:="$(GetAndroidTagForVersion "$androidVersion")"}"
allServices="$(GetAllServicesForTag "$androidVersion" "$tag")";
echo "ServiceName=$serviceName"
echo "ServicePackageName=${servicePackage:="$(GetServicePackageName "$serviceName")"}"
ParseServiceAIDL "$tag" "$allServices" "$servicePackage"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment