6 of 8 I noticed severe flaws in my earlier version of this answer. Rectified them.

In case, you ever decide to go with a command-line way, you can try my approach.

Dependencies

  • Requires 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.

This is 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 || { printf "busybox binary not found or executable permission is not set. Exiting\n" && 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 || { printf "aapt binary not found or executable permission is not set. Exiting\n" && exit; }; }; }

# 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 "\nList of apps with permission android.permission.CAMERA:\n\n";

# From the output we just saved, take each line (package name) and see whether the package has the said permission. Print the app's label and package name when positive. Before printing everything we're also sorting the lines in A-Za-z order. Sorting is not mandatory.
while read line; do 
    [[ `dumpsys package $line | grep 'android.permission.CAMERA'` ]] && perm=1 || perm=0;
    if [[ $perm == 1 ]]; then
        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);
        $busybox printf "$label ($line)\n";
    fi
done < /sdcard/packages.txt | sort

Note: replace highlighted strings with the android permission for which you want to list apps.

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

The greater the apps installed in the system, the greater the time will be for the command to complete execution. In my device, it took around 40 seconds.

Demo output:

List of apps with permission android.permission.CAMERA:

AirDroid (com.sand.airdroid)
Android Live Wallpapers (com.android.wallpaper)
Barcode Scanner (com.google.zxing.client.android)
Camera (com.android.camera2)
CellConnectionService (com.mediatek.CellConnService)
Chrome (com.android.chrome)
...
Webkey (com.webkey)
YGPS (com.mediatek.ygps)
YouTube (com.google.android.youtube)

One-liner

You can reduce the script to one line:

adb shell pm list packages | tr -d '\r'| sed 's/package://g' | while read line; do [[ `adb shell dumpsys package $line | grep 'android.permission.CAMERA'` ]] && echo "$line"; done

Drawbacks:

  • Nothing much, except that you need a *nix OS. Cygwin may work for MS-Windows.
  • You won't be seeing labels but only the package names.

    (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.)

Profit: execution time reduced to ~50%


Bulk removal

Use this command to remove those apps in bulk

adb shell pm list packages | tr -d '\r'| sed 's/package://g' | while read line; do [[ `adb shell dumpsys package $line | grep 'android.permission.CAMERA'` ]] && echo "Removing $line" && adb uninstall $line; done

Expect it to report failure for system apps.


Show App info for all

The following command would display the App info page of the apps having the permission android.permission.CAMERA

adb shell pm list packages | tr -d '\r'| sed 's/package://g' | while read line; do [[ `adb shell dumpsys package $line | grep 'android.permission.CAMERA'` ]] && printf "Launching App info page of $line\n" &&  adb shell am start -d "package:$line" -n com.android.settings/.applications.InstalledAppDetails --activity-multiple-task && sleep 1.5; done

The flag --activity-multiple-task is important to note. It is because of that every App info page for an app would be shown as a new task for the same activity. In essence, App Info page for every app having the fed Android permission can be accessed under the Recent activities and once you close the App Info page of an app using the Back button, the App Info page for an other app would show up because they were consecutively opened.

In this way, you can review the app having the Camera permission and take the actions on it, be it Uninstall or Disable.