⚙️

[React Native] Expo Android アプリの権限を全て削除する

2022/08/20に公開1

Expo SDK 45 から app.config 内で android.blockedPermissions が使えるようになり簡単にアプリの権限を削除できるようになりましたので、Expo アプリ作成直後の初期状態で追加されている権限を全て削除したいと思います。

https://docs.expo.dev/versions/v45.0.0/config/app/#blockedpermissions

アプリ作成

アプリを作成して、ビルドするために適当なパッケージ名を追加します。

$ npx create-expo-app TestApp
app.json
    "android": {
+     "package": "com.example.testapp",
      "adaptiveIcon": {

初期状態の権限を確認

まずは何も変更を加えずにアプリにどのような権限が追加されているのか確認します。

EAS で production ビルドを実行。

$ eas build --profile production --platform android

production ビルドでは aab が作成されるため、bundletool を使用します。

$ java -jar /bundletool-path/bundletool-all-1.11.0.jar dump manifest --bundle TestApp.aab

bundletool の dump manifest でアプリの AndroidManifest.xml を出力します。

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE"/>

development ビルドの場合

development ビルドの場合は apk ファイルが作成されるため権限確認の手順が若干異なります。

$ eas build --profile development --platform android

apk の場合は Android SDK の build-tools 内にある aapt2 を使用します。

$ /android-build-tools-path/aapt2 dump xmltree --file AndroidManifest.xml TestApp.apk

フォーマットが少し異なりますが、AndroidManifest.xml の内容を確認することができます。

A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.SYSTEM_ALERT_WINDOW" (Raw: "android.permission.SYSTEM_ALERT_WINDOW")
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET")
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.READ_EXTERNAL_STORAGE" (Raw: "android.permission.READ_EXTERNAL_STORAGE")
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.VIBRATE" (Raw: "android.permission.VIBRATE")
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.WRITE_EXTERNAL_STORAGE" (Raw: "android.permission.WRITE_EXTERNAL_STORAGE")
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.ACCESS_WIFI_STATE" (Raw: "android.permission.ACCESS_WIFI_STATE")
A: http://schemas.android.com/apk/res/android:name(0x01010003)="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE" (Raw: "com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE")

アプリをインストールして確認

大半はユーザへの同意確認が不要な権限ですが、設定からアプリの詳細情報を表示すると何の権限を使用しているか確認できます。

権限を削除する

app.json の android.blockedPermissions に削除したい権限を全て追加します。

app.json
    "android": {
      "package": "com.example.testapp",
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#FFFFFF"
-     }
+     },
+     "blockedPermissions": [
+       "android.permission.INTERNET",
+       "android.permission.READ_EXTERNAL_STORAGE",
+       "android.permission.SYSTEM_ALERT_WINDOW",
+       "android.permission.VIBRATE",
+       "android.permission.WRITE_EXTERNAL_STORAGE",
+       "com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE"
+     ]
    },

権限を使用する場合は「VIBRATE」といった短い名称で書けますが、削除する場合は AndroidManifest に書かれた通りの「android.permission.VIBRATE」で書く必要があります。

削除後、再度ビルドを実行します。

$ eas build --profile production --platform android
or
$ eas build --profile development --platform android

アプリをインストールして確認

権限が全て無くなりました🎉

権限を削除した事による影響

当然ですが権限を削除することで Expo の機能が正しく動かなくなる場合もあります。

「android.permission.INTERNET」を削除した場合、当たり前ですがネットワークが使用できなくなるため development ビルドの場合開発サーバに接続できずアプリの起動に失敗してしまいます。

ビルド環境に応じて削除する

開発時は何かと権限が必要になってきます。Expo Go クライアントを使用するのも一つの手ですが、本番ビルドの時だけ権限が削除されるようにしてみたいと思います。

最初に追加した blockedPermissions を app.json から削除します。

app.json
    "android": {
    "package": "com.example.testapp",
    "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#FFFFFF"
-   },
-   "blockedPermissions": [
-       "android.permission.INTERNET",
-       "android.permission.READ_EXTERNAL_STORAGE",
-       "android.permission.SYSTEM_ALERT_WINDOW",
-       "android.permission.VIBRATE",
-       "android.permission.WRITE_EXTERNAL_STORAGE",
-       "com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE"
-   ]
+   }
},

app.json と同じ階層に新たに app.config.js を作成します。

app.config.js
export default ({ config }) => {
    if (process.env.EAS_BUILD_PROFILE == 'production') {
        return {
            ...config,
            "android": {
                ...config.android,
                "blockedPermissions": [
                    "android.permission.INTERNET",
                    "android.permission.READ_EXTERNAL_STORAGE",
                    "android.permission.SYSTEM_ALERT_WINDOW",
                    "android.permission.VIBRATE",
                    "android.permission.WRITE_EXTERNAL_STORAGE",
                    "com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE"
                ]
            }
        }
    } else {
        return config;
    }
};

process.env.EAS_BUILD_PROFILE にビルド時のプロファイル名が入っているため、production の時だけ blockedPermissions を追加するようにしました。

これで本番環境向けにビルドした時のみ権限を削除することができました。

Discussion

ピン留めされたアイテム
tadaedotadaedo

追記

app.config.js 内で process.env.EAS_BUILD_PROFILE を使用する場合、

https://docs.expo.dev/build-reference/variables/#built-in-environment-variables

The following environment variables are exposed to each build job — they are not set when evaluating app.config.js locally:
(機械翻訳) 以下の環境変数は、各ビルドジョブに公開されます。app.config.jsをローカルで評価する際には設定されません。

とあり、タイミングによっては環境変数が定義されていないとのこと。eas build 直後の package / bundleIdentifer 判定時に正しい分岐が行えない等の影響あり。

そのため、eas.json 内に独自に PROFILE を定義した方が呼出タイミングを気にせずに分岐が可能っぽい。

https://docs.expo.dev/build-reference/variables/#can-i-share-environment-variables-defined-in

eas.json
-   "production": {}
+   "production": {
+     "env": {
+       "BUILD_PROFILE": "production"
+     }
    }
app.config.js
export default ({ config }) => {
-   if (process.env.EAS_BUILD_PROFILE == 'production') {
+   if (process.env.BUILD_PROFILE == 'production') {
        return {
            ...config,