[Android] permission一覧の取得方法

# 結論

Androidシステムで、定義されているpermissionを確認する方法はいくつか用意されているが、
少なくともAndroid8.1では、下記のどちらかの方法を使うべき。

```
$ adb shell dumpsys package permissions
Permissions:
  Permission [com.google.android.apps.nexuslauncher.permission.READ_SETTINGS] (878caf2):
    sourcePackage=com.google.android.apps.nexuslauncher
    uid=10099 gids=null type=0 prot=signature|privileged
    perm=Permission{2705543 com.google.android.apps.nexuslauncher.permission.READ_SETTINGS}
    packageSetting=PackageSetting{8a0e7c0 com.google.android.apps.nexuslauncher/10099}
  Permission [android.permission.REAL_GET_TASKS] (19f5cf9):
    sourcePackage=android
    uid=1000 gids=null type=0 prot=signature|privileged
    perm=Permission{c4a43e android.permission.REAL_GET_TASKS}
    packageSetting=PackageSetting{df7243c android/1000}
...
```

```
$ adb shell cat /data/system/packages.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
    <version sdkVersion="29" databaseVersion="3" fingerprint="google/sdk_gphone_x86/generic_x86:10/QPP6.190730.005.B1/5775370:userdebug/dev-keys" />
    <version volumeUuid="primary_physical" sdkVersion="29" databaseVersion="3" fingerprint="google/sdk_gphone_x86/generic_x86:10/QPP6.190730.005.B1/5775370:userdebug/dev-keys" />
    <permission-trees>
        <item name="com.google.android.googleapps.permission.GOOGLE_AUTH" package="com.google.android.gsf" />
    </permission-trees>
    <permissions>
        <item name="com.google.android.apps.nexuslauncher.permission.READ_SETTINGS" package="com.google.android.apps.nexuslauncher" protection="18" />
        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />="com.google.android.apps.nexuslauncher" protection="18" />
...
```

# NGな方法

[Android Developer](https://developer.android.com/guide/topics/security/permissions.html?hl=ja#defining)には、下記のように記載がある。
しかし、少なくともAndroid8.1では、**全てのpermissionは出力されない**ので注意。

> 設定アプリ、またはシェルコマンド adb shell pm list permissions を使って現在システムに定義されているパーミッションをご覧いただけます。

なお、`adb shell cmd package list permissions`も、内部実装は共通なので、同じくすべてのpermissionは出力されない。


# 何が違うのか

まず、`pm list permissions`は、permission groupに**属さない**permissionしか表示しない実装になっている。
（ちなみに、[pmコマンドのマニュアル](https://developer.android.com/studio/command-line/adb?hl=JA)では、「既知のすべてのパーミッションを出力します。」となっている）

例えば、`CALL_PHONE` permissionは、`PHONE` groupに属しているので、表示されない。

`-g`オプションをつけることで、既知のpermission groupそれぞれに対して、所属するpermissionをすべて出力することができるが、これも完璧ではない。
permissionの定義がイケてなくて、存在しないpermission groupに属していることにしてしまっている場合、リストアップされない。

具体的には、`android.permission.DOWNLOAD_WITHOUT_NOTIFICATION`というpermissionは
`android.permission-group.NETWORK`というグループに属すると定義されているが、
`android.permission-group.NETWORK`が定義されていないため、`pm list permissions -g`では表示されない。

一方で`dumpsys package permissions`は、
素直に`/data/system/packages.xml`の中の`permissions`要素をリストアップしているため、同じ問題が発生しない。

なお実装は、以下のあたり。

`pm list permissions`: frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.javaの`runListPermissions`, `doListPermissions`
`dumpsys package permissions`: frameworks/base/services/core/java/com/android/server/pm/Settings.javaの`dumpPermissionsLPr`


# おわりに

PackageManagerはdumpsysとpm(cmd)が両方似たような領域でがんばっていて、どちらを使うべきかいちいち悩む。。。
今回のpermissionに関しても、網羅性という観点では`dumpsys package permissions`一択だが、
`pm list permissions`には`-f`オプションをつけることでlabelとdescriptionを出力してくれるという便利な機能があり、
`dumpsys`だけ使えりゃいい、というわけでもない。
なんでこんなことになってしまっているのか。。。