Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bashenk/b538ce0a60efe6a9c3b446683744d598 to your computer and use it in GitHub Desktop.
Save bashenk/b538ce0a60efe6a9c3b446683744d598 to your computer and use it in GitHub Desktop.
POSIX compliant and portable fork. Now has been updated with more optional parameters, more optimizations, and better error handling. If -t or -v are specified, an android device is not required.
#!/usr/bin/env sh
set -u
# https://stackoverflow.com/questions/2683279/how-to-detect-if-a-script-is-being-sourced#28776166
sourced=0
if [ -n "${ZSH_EVAL_CONTEXT-}" ]; then
case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac;
elif [ -n "${KSH_VERSION-}" ]; then
if [ "$(cd "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ]; then
sourced=1
fi;
elif [ -n "${BASH_VERSION-}" ]; then
if (return 0 2>/dev/null); then
sourced=1
fi;
else # All other shells: examine $0 for known shell binary filenames
# Detects `sh` and `dash`; add additional shell filenames as needed.
case ${0##*/} in sh|dash) sourced=1;; esac;
fi;
# if [ $sourced -eq 0 ]; then set -e; fi
say () { if [ -z "${_quiet-}" ]; then printf '%s\n' "$@"; fi; }
quit () {
if [ $# -ge 1 ] && [ "$1" -eq "$1" ] 2>/dev/null; then exitCode="$1"; shift; fi;
if [ $# -ge 1 ] ; then printf '%s\n' "$@" 1>&2;fi;
if [ $sourced -eq 1 ]; then kill -INT 0; else exit "${exitCode:-0}";fi;
}
show_help () {
cat <<-EOF
Get an indexed list of service calls for a particular Android version and Android service.
$(basename "$0") [-s SERIAL | -t TAG | -v NUM] [-g GREP_ARGS] [-q] SERVICENAME
$(basename "$0") -a [-s SERIAL | -t TAG | -v NUM] [-g GREP_ARGS] [-q]
-a Get the output for every Android service available on the specified Android OS version
-h Display this help text
-g If provided, evaluate the provided argument with grep on each result
-q Limit output to only the service calls
-s Specify the ADB serial of the device to use for which to get the Android OS version
-t Specify the Android version tag from which to search for the service calls
-v Specify the Android OS version from which to get the latest version tag to use
EOF
}
while getopts ":ahqg:s:t:v:" OPT; do
case "$OPT" in
a ) _all=0 ;;
h ) show_help; quit ;;
g ) _grep="$OPTARG" ;;
q ) _quiet=0 ;;
s ) _adbSerial="$OPTARG" ;;
t ) _androidVersionTag="$OPTARG" ;;
v ) _androidVersion="$OPTARG" ;;
\? ) quit 2 "Argument '-$OPTARG' not recognized" ;;
esac
done
shift $((OPTIND-1))
if [ -z "${_all-}" ]; then
if [ $# -eq 0 ]; then
show_help; quit 2 "Please provide a service name argument"
else
serviceName="$1"
fi
fi
assert_executables_are_available () {
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]}" ] || quit 1 "Exiting due to insufficient dependencies"
exec sudo apt install "$bins"
}
assert_executables_are_available grep sed wget
googleSource="https://android.googlesource.com"
repoPath="platform/frameworks/base"
get_first_adb_device_serial () { adb devices | sed -e '1d' -e '$d' -e '2s/[[:space:]].*device$//' -e 'q'; }
adb_shell () { eval adb "${_adbSerial:+-s $_adbSerial }"wait-for-device shell -T -x "$*" | tr -d '\r'; }
get_google_source_file () { wget -qO - "$googleSource/$repoPath/+/$1/$2?format=text" | base64 -d; }
get_android_tag_for_version () {
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 "${1:?No Android version was specified in get_android_tag_for_version}" | sed -e 'y/././')/p;d" | tail -n 1)"
[ -n "$localTag" ] || quit 1 "TAG not valid!" "List of valid tags:" "$allTags"
echo "${localTag:?TAG not valid!\nList of valid tags:\n$allTags}"
}
get_service_package_name () {
packageName=$(adb_shell service list | sed -e '1d' -e 's/[0-9]*[[:space:]]'"${1:?No package name was specified in get_service_package_name}"': \[\(.*\)\]/\1/p' -e 'd');
echo "${packageName:?Unable to find service package name for "$1"}";
}
get_all_services_for_tag () {
if [ "${_androidVersion:?Android version was unspecified}" -gt 8 ]; then file='Android.bp'; else file='Android.mk'; fi
get_google_source_file "${1:?No tag was specified in get_all_services_for_tag}" "$file" | tr -d ' \\\t,"' | sed -e '/\.aidl$/!d' -e '/^gen:/d' | sort -u
}
parse_service_aidl () {
packageFilePath="$(echo "${3:?No package name was specified in parse_service_aidl}" | tr '.' '/')\.aidl"
servicePath="$(echo "${2:?Services list was not specified in parse_service_aidl}" | grep -m 1 "$packageFilePath\$")"
get_google_source_file "${1:?No tag was specified in parse_service_aidl}" "$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' | { if [ -n "${_grep-}" ]; then cat -n | eval grep "$_grep"; else cat -n; fi; }
}
if [ -n "${_androidVersionTag-}" ] && [ "$_androidVersionTag" -eq "$_androidVersionTag" ] 2>/dev/null; then
quit 1 "Tag was specified as '$_androidVersionTag'" "Are you sure you did not mean to specify Android OS Version (-v)?"
fi
if [ -n "${_androidVersion-}" ]; then
:
elif [ -n "${_androidVersionTag-}" ]; then
_androidVersion="$(echo "${_androidVersionTag}" | sed 's/^[-a-zA-Z]*\([0-9]*\)\.\?.*$/\1/')"
if [ "$_androidVersion" -ne "$_androidVersion" ] 2>/dev/null; then quit 1 "Failed to parse Android version from tag."; fi;
else
# Use _adbSerial variable for device, or grab the first available device's serial number
assert_executables_are_available adb
if [ -z "${_adbSerial-}" ]; then
_adbSerial="$(adb devices | sed -e '1d' -e '$d' -e '2s/[[:space:]].*device$//' -e 'q')";
fi
say "ADB Serial = ${_adbSerial:?No devices found}" 1>&2;
_androidVersion="$(adb_shell getprop ro.build.version.release)"
fi
if [ -z "${_androidVersionTag-}" ]; then
_androidVersionTag="$(get_android_tag_for_version "$_androidVersion")"
fi
allServices="$(get_all_services_for_tag "$_androidVersionTag")"
if [ -z "${allServices-}" ]; then
quit 2 "Failed to find services. It is likely an invalid tag was specified"
fi
say "Android OS Version = ${_androidVersion:?Android OS Version was null}" 1>&2;
say "Android Version Tag = ${_androidVersionTag:?Android Version Tag was null}" 1>&2;
if [ -n "${_all-}" ]; then
for srv in $allServices; do
result="$(get_google_source_file "$_androidVersionTag" "$srv" | 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' | { if [ -n "${_grep-}" ]; then cat -n | eval grep "$_grep" || true; else cat -n; fi; })"
if [ -n "${result-}" ]; then
printf '%s\n' "Service \"${srv##*/}\":" "$result"
fi
done
else
assert_executables_are_available adb
if [ -z "${serviceName-}" ]; then say "ServiceName=$serviceName" 1>&2; fi
if [ -z "${servicePackage-}" ]; then say "ServicePackageName=${servicePackage:="$(get_service_package_name "$serviceName")"}" 1>&2; fi
parse_service_aidl "$_androidVersionTag" "$allServices" "$servicePackage"
fi
@T-vK
Copy link

T-vK commented Mar 23, 2021

Starting with Android 11 you have to actually search through the entire directory tree to find the aidl files because Android.bp only contains a bunch of glob expressions for the aidl file paths. I got that working in android-svc here:

https://github.com/T-vK/android-svc/blob/7c9d9c590e7ecbe8170086f3aafde54fe5ffd1ce/android-svc-lib.sh#L260

I'm probably not POSIX compliant, but feel free to use that information to update your get_all_services_for_tag function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment