Android Kitkat 4.4.2〜4.4.4向けAppOpsの移植

#アプリの権限操作。
今更Kitkatである。しかしアプリ権限操作は重要な機能なので纏めておく。

Androidに公式のアプリ権限機能が Jerry Beans 4.3に搭載された。
これがApp Opsである。しかしこの時点では隠し機能であり、ショートカットアプリから呼び出すことができる。
4.4.1まではこの隠し機能へショートカットのみでアクセスすることができていたのだが、
4.4.2からはショートカットだけではアクセスできなくされてしまった。
[スラドの記事](http://security.srad.jp/story/13/12/14/1847238/?utm_source=twitterfeed&utm_medium=twitter)によると、App OpsはGoogleが誤って公開してしまった実験段階の機能であるらしい。

しかしApp Ops自体は4.4.2〜4.4.4にも残っており、設定アプリを少し弄るとこれを復活させることができる。

…カスタムロム等を使用している場合は最初からこの機能へのアクセスを可能にしている場合がある。
一度設定アプリを隅々まで探索し、App Ops機能が無いことを確認してから実験に移ることを推奨する。
筆者の環境はCM11(4.4.4)だったのだが、この記事を書き終える直前に設定アプリからプライバシー→プライバシーガード→メニュー→詳細設定でApp Opsが起動できることを確認した。
骨折り損のくたびれ儲けにならぬよう。

##早速Settings.apkを弄る。
[こちらのWebページ](https://forum.xda-developers.com/showthread.php?t=2566695)が非常に参考になる。というよりこの記事は大半翻訳しただけ…。
4.4.2とあるが、[4.4.3、4.4.4でも同様の手順で対応可能](https://forum.xda-developers.com/google-nexus-5/themes-apps/mod-4-4-3-modified-settings-apk-app-ops-t2780142)なようだ。

###用意するもの
1. [apktool](https://ibotpeaches.github.io/Apktool/)
2. Settings.apk
3. framework-res.apk

apktoolはJava1.7が必要になるので各自インストールすること。
ここではLinuxで実験したのでコマンドもbash向けであるが、他のプラットフォームでもほとんど変わらず実行できるはずだ。
2と3に関しては端末によっては名前が違ったり、おいてある場所が違ったり、3の場合は複数存在したりすることもあるので注意。といってもバリバリにカスタムロムを作っている方たちならば問題ないはずだ。
当然だが問題が起こった時のためにバックアップは確実にとっておくこと。
筆者の環境ではSettings.apkは`/system/priv-app`に、framework-res.apkは`/system/framework`にあった。
異なっていた場合は当然以下の手順も適宜置き換えて考えるように。

###手順1.Settings.apkをデコンパイルする
上記の３つを取り出したら同じディレクトリに置き、以下のコマンドを実行する。

```bash
$ ./apktool if framework-res.apk
$ ./apktool d Settings.apk
$ mv Settings.apk Settings_bak.apk
```

これでSettings.apkが展開される。
一行目は無くてもデコンパイルできることが多いが、無しでデコンパイルすると再度コンパイルできないので必ずデコンパイルする前に実行しておく。
###手順2.Settings.smaliの編集・一箇所目

16進数の値は環境によって違うはずなので気にする必要はない。

```Settings.smali
(省略)
.method static constructor <clinit>()V
    .locals 3

    .prologue
    const/4 v2, 0x0

    .line 170
    sput-boolean v2, Lcom/android/settings/Settings;->sShowNoHomeNotice:Z

    .line 435
    const/16 v0, 0x3b

    new-array v0, v0, [Ljava/lang/String;
(省略)
```

`sput-boolean v2, Lcom/android/settings/Settings;->sShowNoHomeNotice:Z`で検索をかけるとすぐに場所がわかると思われる。

余談だが、Bashでファイルの中まで検索をかけたい場合は以下のコマンドを実行すれば良い。
Settings.smaliというファイルが無いときはこれで探すことができる。
以降の手順もファイルがない、あってもそのファイルに該当する箇所がないケースも少なくないので覚えておくと良い。

```bash
$ grep -rnw . -e "sput-boolean v2, Lcom/android/settings/Settings;->sShowNoHome
Notice:Z"
```

さて、今回の修正で重要なのは`const/16 v0, 0xXX`の`XX`の値だ。
この値に**1を加算**する。
当然16進数なので16進数で計算するように。

```Settings.smali
(省略)
.method static constructor <clinit>()V
    .locals 3

    .prologue
    const/4 v2, 0x0

    .line 170
    sput-boolean v2, Lcom/android/settings/Settings;->sShowNoHomeNotice:Z

    .line 435
    const/16 v0, 0x3c

    new-array v0, v0, [Ljava/lang/String;
(省略)
```
このような感じだ。`const/16 v0, 0x3b`を`const/16 v0, 0x3c`に修正している。
数字は環境によって異なるが、とにかく1を加算すれば良い。
###手順3.Settings.smaliの編集・二箇所目

```Settings.smali
    const/16 v1, 0x2b

    const-class v2, Lcom/android/settings/inputmethod/KeyboardLayoutPickerFragment;

    invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String;

    move-result-object v2

    aput-object v2, v0, v1

    const/16 v1, 0x2c

```

`const-class v2, Lcom/android/settings/inputmethod/KeyboardLayoutPickerFragment;`で検索をかけると出てくると思われる。
`aput-object v2, v0, v1`の後(筆者の環境ではその下の`const/16 v1, 0x2c`との間)に

```smali
    const/16 v1, 0x3b

    const-class v2, Lcom/android/settings/applications/AppOpsSummary;

    invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String;

    move-result-object v2

    aput-object v2, v0, v1
```

を追記する。とにかく**`aput-object v2, v0, v1`の後**であればよい。
この時、一行目の`const/16 v1, 0xXX`の`XX`は**一箇所目の編集箇所の編集前の値**を入れる。
今回の例では一箇所目を`0x3b`から`0x3c`に修正したのでここに入る値は`0x3b`となる。

```Settings.smali
    const/16 v1, 0x2b

    const-class v2, Lcom/android/settings/inputmethod/KeyboardLayoutPickerFragment;

    invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String;

    move-result-object v2

    aput-object v2, v0, v1
    
    const/16 v1, 0x3b

    const-class v2, Lcom/android/settings/applications/AppOpsSummary;

    invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String;

    move-result-object v2

    aput-object v2, v0, v1

    const/16 v1, 0x2c

```

このようになるはずだ。
###手順4.values/strings.xmlの編集

```xml
<string name="app_ops_settings_summary">Manage app permissions</string>
```

を`</resources>`の一行手前あたりに追記すれば良い。
###手順5. xml/security_settings_misc.xmlの編集
追記する内容は

```xml
<Preference android:title="@string/app_ops_settings" android:key="app_ops_summary" android:summary="@string/app_ops_settings_summary" android:fragment="com.android.settings.applications.AppOpsSummary" />
```

これをdevice admin titleの後に追記する。
…なのだが筆者の環境にはsecurity_settings_misc.xmlはなかった。
こういう時は手順2で解説した


```bash
$ grep -rnw . -e "device admin"
```

を使う。
すると`security_settings.xml`のみが候補に上がった。

```security_settings.xml
(省略)
    <PreferenceCategory android:persistent="false" android:title="@string/device_admin_title" android:key="device_admin_category">
        <Preference android:persistent="false" android:title="@string/manage_device_admin" android:summary="@string/manage_device_admin_summary" android:fragment="com.android.settings.DeviceAdminSettings" />
        <CheckBoxPreference android:persistent="false" android:title="@string/install_applications" android:key="toggle_install_applications" android:summaryOn="@string/install_unknown_applications" android:summaryOff="@string/install_unknown_applications" />
(省略)
```

とあるので、

```xml
        <Preference android:persistent="false" android:title="@string/manage_device_admin" android:summary="@string/manage_device_admin_summary" android:fragment="com.android.settings.DeviceAdminSettings" />`
```

の後に

```xml
        <Preference android:title="@string/app_ops_settings" android:key="app_ops_summary" android:summary="@string/app_ops_settings_summary" android:fragment="com.android.settings.applications.AppOpsSummary" />
```

を追記すれば良い。

```security_settings.xml
(省略)
    <PreferenceCategory android:persistent="false" android:title="@string/device_admin_title" android:key="device_admin_category">
        <Preference android:persistent="false" android:title="@string/manage_device_admin" android:summary="@string/manage_device_admin_summary" android:fragment="com.android.settings.DeviceAdminSettings" />
        <Preference android:title="@string/app_ops_settings" android:key="app_ops_summary" android:summary="@string/app_ops_settings_summary" android:fragment="com.android.settings.applications.AppOpsSummary" />
        <CheckBoxPreference android:persistent="false" android:title="@string/install_applications" android:key="toggle_install_applications" android:summaryOn="@string/install_unknown_applications" android:summaryOff="@string/install_unknown_applications" />
(省略)
```

###手順6.Settings.apkをコンパイルする
以下のコマンドを実行する。

```bash
$ ./apktool b -c Settings -o Settings.apk
```

うまくデコンパイル・編集ができていればビルドに成功するはずだ。
後は元のSettings.apkと置き換えれば完了。
今回の例だと設定→セキュリティ→アプリの操作状況でApp Opsを起動できるようになっている。
##他のAndroidバージョンでのアプリ権限操作
余談だが、Android Lollipop 5には最初から[権限操作機能](http://holic.hateblo.jp/entry/2015/08/13/142813)が[ついている](https://www.quora.com/How-do-I-control-app-permissions-on-Android-5-1-1-smartphones)らしい。これについては情報が少なく、筆者にもよくわかっていない。
Android Marshmallow 6以降はアプリが権限を要求した段階でユーザーに権限を渡してよいか尋ねられる仕組みになった。

他にもRoot化端末向けには[Xposed](http://repo.xposed.info/)のモジュールに[XPrivacy](http://repo.xposed.info/module/biz.bokhorst.xprivacy)というものがある。これもアプリが権限を要求した段階でユーザーに権限を渡してよいか尋ねられる仕組みだが、Android6以降の公式権限管理機能よりも細かい調整が可能になっている。
これはRoot化されており、Xposedがインストール済みの環境ならばAndroid 4.2.2以下でも使用可能だ。