Android - How can I find which apps/packages reference a given permission using command line?

The first section is ideally supposed to be OS (on PC) independent. The solution heavily depends upon the output of package service. It has been successfully tested on Android 4.2.1, 5.0.2 and 5.1.1 - all these versions are not heavily modified from stock Android.

Dependencies

  • Requires adb to be setup in PC.
  • Requires busybox binary. If the device is rooted, install Busybox app. Else, download busybox binary from official source, rename the binary to busybox, set Linux compatible executable permission on that binary for everyone and move it into device using

    adb push LOCAL_FILE /data/local/tmp/   # LOCAL_FILE is the file path where busybox binary is located in PC
    
  • Requires aapt binary. If you're running a CM or its derivative ROM then ignore this requirement. Otherwise, for Android 4.x, you can consider downloading the binary from here, rename the binary to aapt, set Linux compatible executable permission on that binary for everyone and move it into device using

    adb push LOCAL_FILE /data/local/tmp/   # LOCAL_FILE is the file path where busybox binary is located in PC . 
    

    Android 5.x users: ask Google for assistance.

Here's my little script:

#!/system/bin/sh
# Check if the busybox binary exists under /data/local/tmp/ or /system/xbin. Set the detected binary's path into the variable busybox or exit if file doesn't exist or executable permission is not set
[[ -x /data/local/tmp/busybox ]] && busybox=/data/local/tmp/busybox || { [[ -x /system/xbin/busybox ]] && busybox=/system/xbin/busybox || { date +'busybox binary not found or executable permission is not set. Exiting' && exit; }; }

# Check if the aapt binary exists under /data/local/tmp or /system/bin or /system/xbin. Set the detected binary's path into the variable aapt or exit if file doesn't exist or executable permission is not set
[[ -x /data/local/tmp/aapt ]] && aapt=/data/local/tmp/aapt || { [[ -x /system/bin/aapt ]] && aapt=/system/bin/aapt || { [[ -x /system/xbin/aapt ]] && aapt=/system/xbin/aapt || { date +'aapt binary not found or executable permission is not set. Exiting' && exit; }; }; }

# Validate input
! [[ "$1" == +([0-9a-zA-Z._]) ]] && { $busybox printf 'Permission field should not be empty or contain anything beyond these characters: a-zA-Z0-9._' && exit; } || perm=$1;

# List package name of all the installed apps and save them in the file packages.txt under /sdcard
pm list packages | $busybox sed 's/^package://g' | $busybox sort -o /sdcard/packages.txt
$busybox printf "List of apps defining and/or depending on the permission $perm:\n\n";

# Take each line (a package name) from the file packages.txt. In the output of package service for that package name, see if the permission is granted or defined and set appropriate variable state. For different states, we're either dumping the label of the app using aapt, printing the status of define/granted permissions for package or simply moving on.
while read line; do 
    [[ `dumpsys package $line | grep -Eo "^[ ]+$perm"` ]] && granted=1 || granted=0;
    [[ `dumpsys package $line | grep -Eo "^[ ]+Permission[ ]+\[$perm\][ ]+\([a-zA-Z0-9]+\):"` ]] && defined=1 || defined=0;
    [[ $granted == 1 || $defined == 1 ]] && path=$(pm path $line | $busybox sed 's/^package://g') && label=$($aapt d badging $path 2>&1 | $busybox sed -ne '/application: label=/p' | $busybox cut -d "'" -f2);
    [[ $granted == 1 && $defined == 1 ]] && $busybox printf "$label ($line)\nDefined: Yes\nGranted: Yes\n\n";
    [[ $granted == 1 && $defined != 1 ]] && $busybox printf "$label ($line)\nDefined: No\nGranted: Yes\n\n";
    [[ $granted != 1 && $defined == 1 ]] && $busybox printf "$label ($line)\nDefined: Yes\nGranted: No\n\n";
done < /sdcard/packages.txt 

Save the script in PC into a file named perm_script.sh and move it into /sdcard using

adb push LOCAL_FILE /sdcard/   # LOCAL_FILE is the  path where you saved that file into PC

Run that file

adb shell sh /sdcard/perm_script.sh PERMISSION   # replace PERMISSION with the android permission for which apps are to be shown

Demo output:

List of apps defining and/or depending on the permission android.permission.FLASHLIGHT:

Android System (android)
Defined: Yes
Granted: No

Automagic Premium (ch.gridvision.ppam.androidautomagic)
Defined: No
Granted: Yes

MacroDroid (com.arlosoft.macrodroid)
Defined: No
Granted: Yes

Google+ (com.google.android.apps.plus)
Defined: No
Granted: Yes
...
Bluetooth (com.mediatek.bluetooth)
Defined: No
Granted: Yes

DS Battery Saver Pro (com.rootuninstaller.batrsaverpro)
Defined: No
Granted: Yes

Webkey (com.webkey)
Defined: No
Granted: Yes

Do note that all those apps and their permissions among other things can also be found in the file /data/system/packages.xml.


(To get app's label using its package name, use GAThrawn's answer - works if only the app is available in Play Store; use Izzy's answer - works for any installed app.)