【Android M】アプリ内からPermission設定画面に遷移する(失敗編)

Android Mの厄介な機能としてRuntime Permissionsが入りますよね。

詳しくは公式のドキュメントを見れば、だいたいどんな機能なのかわかります。

**[Permissions](https://developer.android.com/preview/features/runtime-permissions.html)**

開発者的には、これに対応させるのってすげー大変なのですよ。。。

Permissionの許可・非許可ってアプリの設定画面からユーザが自由に操作できるんですよね。

例えば、標準のカメラアプリのPermission設定画面がこれ。

![Screenshot_20150914-102734.png](https://qiita-image-store.s3.amazonaws.com/0/43637/359115e6-5562-9ce3-2fa1-60563d685781.png)

んで今回、この**Permission設定画面にアプリ内からIntentを作って遷移することができるのか？**という検証をしてみた。

# Permission設定画面にアプリ内からIntentを作って遷移することができるのか？

Permission設定画面に遷移するために、調べて見たことをまとめてみた。

* 遷移するIntentのActionは、**android.intent.action.MANAGE_APP_PERMISSIONS**
* 起動するActivityは、**com.google.android.packageinstaller/com.android.packageinstaller.permission.ui.ManagePermissionsActivity**
* ManagePermissionsActivityに**AppPermissionsFragment**というFragmentがAttachされている
* IntentのデータにKeyが**android.intent.extra.PACKAGE_NAME**で、値に**アプリのパッケージ名**が入ってるっぽい

## adb shell amでPermission設定画面を開けるか試してみた

以下の```adb shell am```でPermission設定画面を開けるか試してみた。

```bash
adb shell am start -a android.intent.action.MANAGE_APP_PERMISSIONS -e android.intent.extra.PACKAGE_NAME <Package Name>

e.g.)
adb shell am start -a android.intent.action.MANAGE_APP_PERMISSIONS -e android.intent.extra.PACKAGE_NAME com.kouzoh.mercari
```

### 結果(Permission設定画面 開いた)

**android.intent.extra.PACKAGE_NAME**に指定したパッケージ名のアプリのPermission設定画面が開いた。

うん、**開いた！**

![Screenshot_20150914-184455.png](https://qiita-image-store.s3.amazonaws.com/0/43637/3c3dccc3-7674-23e9-8b17-0c1b1457df44.png)

どうやら```adb shell am```からなら開けるっぽい。

では、お次は目的のアプリ内から開けるかどうか。

## アプリ内からIntentでPermission設定画面に遷移することができるのか試してみた

```adb shell am```の結果から、以下のようなコードでアプリ内からPermission設定画面に遷移できるのではないかと考えた。

```java
Intent i = new Intent();
i.setAction("android.intent.action.MANAGE_APP_PERMISSIONS");
i.putExtra("android.intent.extra.PACKAGE_NAME", getPackageName());
startActivity(i);
```

### 結果 (SecurityExceptionが起きて遷移できない)

startActivityを実行したところ、**SecurityException**が起きて**Permission設定画面に遷移できなかった**。


```java
Process: com.mercariapp.mercari, PID: 23142
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mercariapp.mercari/com.kouzoh.mercari.activity.TestActivity}: java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MANAGE_APP_PERMISSIONS cmp=com.google.android.packageinstaller/com.android.packageinstaller.permission.ui.ManagePermissionsActivity (has extras) } from ProcessRecord{1573c3 23142:com.mercariapp.mercari/u0a102} (pid=23142, uid=10102) requires android.permission.GRANT_RUNTIME_PERMISSIONS
	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
	at android.app.ActivityThread.-wrap11(ActivityThread.java)
	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
	at android.os.Handler.dispatchMessage(Handler.java:102)
	at android.os.Looper.loop(Looper.java:148)
	at android.app.ActivityThread.main(ActivityThread.java:5417)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MANAGE_APP_PERMISSIONS cmp=com.google.android.packageinstaller/com.android.packageinstaller.permission.ui.ManagePermissionsActivity (has extras) } from ProcessRecord{1573c3 23142:com.mercariapp.mercari/u0a102} (pid=23142, uid=10102) requires android.permission.GRANT_RUNTIME_PERMISSIONS
	at android.os.Parcel.readException(Parcel.java:1599)
	at android.os.Parcel.readException(Parcel.java:1552)
	at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:2658)
	at android.app.Instrumentation.execStartActivity(Instrumentation.java:1507)
	at android.app.Activity.startActivityForResult(Activity.java:3917)
	at android.app.Activity.startActivityForResult(Activity.java:3877)
	at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:820)
	at android.app.Activity.startActivity(Activity.java:4200)
	at android.app.Activity.startActivity(Activity.java:4168)
	at com.kouzoh.mercari.activity.TestActivity.onCreate(TestActivity.java:60)
	at android.app.Activity.performCreate(Activity.java:6237)
	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
```

メッセージを見る感じ**android.permission.GRANT_RUNTIME_PERMISSIONS**のPermissionが必要そう。

GRANT_RUNTIME_PERMISSIONSを詳しく調べてないのでわからないですが、System Process関連しか使えないPermission系かもしれない。

とりあえずアプリ内から直接特定アプリのPermission設定画面には遷移できなさそう。(今のところは)

# 結論

* adb shell amコマンドからなら特定アプリのPermission設定画面に遷移できる
* アプリ内で作ったIntentで遷移しようとするとSecurityExceptionが起きて遷移できない
 * 遷移するには何かしたら権限が必要みたい
 * android.permission.GRANT_RUNTIME_PERMISSIONS?


# おまけ

今回、アプリ内からPermission設定画面に遷移させるために使ったコードのActionとかKeyがベタ書きで書かれているけど、SDK 23のIntentクラスにこれらは定義されています。

```java
Intent i = new Intent();
i.setAction("android.intent.action.MANAGE_APP_PERMISSIONS");
i.putExtra("android.intent.extra.PACKAGE_NAME", getPackageName());
startActivity(i);
```

こんな感じで定義されている。けど、hideなので直接使えないんだよね。リフレクションしたくないし・・・。

```java
Intent.java(SDK 23から一部抜粋)

/**
 * Intent extra: An app package name.
 * <p>
 * Type: String
 * </p>
 *
 * @hide
 */
@SystemApi
public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";


**
 * Activity action: Launch UI to manage the permissions of an app.
 * <p>
 * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permissions
 * will be managed by the launched UI.
 * </p>
 * <p>
 * Output: Nothing.
 * </p>
 *
 * @see #EXTRA_PACKAGE_NAME
 *
 * @hide
 */
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_APP_PERMISSIONS =
        "android.intent.action.MANAGE_APP_PERMISSIONS";

```

# まとめ

何かいい情報や「こうやれば遷移できた！」って情報あればフィードバックもらえると嬉しいです！

引き続きPermission周りの調査続けてみる。