Android12対応でexported属性を設定したのに怒られた時の対処法

Android12から、セキュリティ面の改善を目的として、インテントフィルタを使用するアクティビティやサービスなどのコンポーネントで`android:exported`属性を明示的に宣言する必要があります。

https://developer.android.com/about/versions/12/behavior-changes-12?hl=ja#exported

しかし私の手元では、インテントフィルタを使用する全てのコンポーネントで`android:exported`属性を宣言したにも関わらず**「exported属性を指定してくれ」とエラーになってコンパイルできない事象**がありました。

そこでこの記事ではどのようにしてその事象を解決したかを紹介します。


### 結論

自分で用意したManifestではexported属性をちゃんと宣言してても、依存ライブラリでのManifestで条件を満たしていない場合、最終的にmergeされたManifest内で条件を満たさないことになるため、コンパイルエラーになります。

条件を満たしていないAndroidManifestを持つライブラリやモジュールを探し、(外部ライブラリの場合は)exported属性を宣言されてるバージョンにアップデートすることで解決できるかと思います。

### 前提条件

- [Android12 SDKのセットアップ](https://developer.android.com/about/versions/12/setup-sdk?hl=ja#groovy)が完了している
- 自分で定義したAndroidManifestファイル内で、インテントフィルタを使用する全てのコンポーネントで`android:exported`属性を宣言している


### エラー内容

まず、コンパイルできなかった時のエラー内容は以下です。

```terminal
Manifest merger failed with multiple errors, see log
Error: Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined

```

「Android12以上をターゲットにするときは、インテントフィルターが定義されてるコンポーネントには`android:exported`を明示してな」と怒られています。

### なぜエラーになるか

でも、どんなに辛抱強くAndroidManifest内を見ても、インテントフィルターを定義してるコンポーネントでは、やっぱり全部exportedを宣言してるな。。。なぜだ。。。

とここで、先ほどのエラーログをもう一度見ると、`Manifest merger failed with multiple errors`とあります。

そういえばそうだ、AndroidManifestは一つではなく、**ライブラリの中にも含まれている**んだった。

[ドキュメント](https://developer.android.com/studio/build/manifest-merge?hl=ja)にはこうあります。

> APK ファイルに含めることのできる AndroidManifest.xml ファイルは 1 つだけに限られますが、Android Studio プロジェクト内には、メイン ソースセットや、ビルド バリアント、インポート ライブラリによって提供される複数のマニフェスト ファイルを含めることができます。そのため、アプリをビルドする際、Gradle ビルドは、すべてのマニフェスト ファイルを単一のマニフェスト ファイルにマージし、APK 内にパッケージ化します。

なので、最終的にマージされたAndroidManifestを見てみます。


### Merged Manifestを見る

メインモジュール(`app`など)の`AndroidManifest.xml`を開くと、実は下に`Merged Manifest`というタブがあり、そこからプロジェクト内の、依存ライブラリも含んだ全てのAndroidManifest.xmlをマージしたManifestを見ることができます。

![スクリーンショット 2021-07-28 1.07.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214755/d2113b6b-0f6b-991c-a734-3a348f50bc67.png)

この統合されたManifestファイルを改めて眺めていくと。。。

![スクリーンショット 2021-07-27 18.30.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214755/9fc4cb95-ba0e-eabe-dfc2-88690544164f.png)

おっ、この3つのactivityコンポーネントで、**インテントフィルターが定義されてるのにexportedの宣言がない**ことが分かります。

原因はここにあった！！

で、こいつらの`name`を見てみると、[androidx.test:core](https://mvnrepository.com/artifact/androidx.test/core)のライブラリであることが分かります。

### 依存ライブラリをアップデートする

Android Code Searchを使って`androidx.test:core`のライブラリのアーティファクトを遡っていくと、`androidx.test:core:1.3.1-alpha01`から、Manifestの各コンポーネントでexported属性が宣言されてるのが分かります。

https://cs.android.com/androidx/android-test/+/refs/tags/androidx-test-1.3.1-alpha01:core/java/androidx/test/core/AndroidManifest.xml

もし私のプロジェクトで`androidx.test:core`の依存関係をbuild.gradleに宣言していたら話は早かったのですが、残念なことに私のプロジェクトではこのライブラリは直接宣言しておらず、なんのライブラリが間接的にこのtest:coreに依存しているのかを探さなければなりません。

そこで、テスト関連の他のライブラリが何に依存しているのかをmavenや[AndroidX Tech](https://androidx.tech/)を使って一個一個確認していったところ、

- [androidx.test.ext:junit:1.1.1 が androidx.test:core:1.2.1に依存している](https://androidx.tech/artifacts/test.ext/junit/1.1.1)
- [androidx.test.espresso:espresso-intents:3.1.1 が androidx.test:core:1.1.0に依存している](https://androidx.tech/artifacts/test.espresso/espresso-intents/3.1.1)
- [androidx.fragment:fragment-testing:1.3.3 が androidx.test:core:1.3.0に依存している](https://androidx.tech/artifacts/fragment/fragment-testing/1.3.3)

ということが分かり、これらを、不要なものは削除しつつ、`androidx.test:core:1.4.0`(1.3.1-alpha01以降)に依存しているバージョンまで上げることで解消することができました。

なお、私のプロジェクトでは、これ以外にも、

- `com.squareup.leakcanary:leakcanary-android:1.5.1`もManifestFileでexported属性が宣言されておらず、バージョンアップをしました。

### 結論

自分で用意したManifestではexported属性をちゃんと宣言してても、依存ライブラリでのManifestで条件を満たしていない場合、最終的にmergeされたManifest内で条件を満たさないことになるため、コンパイルエラーになります。

条件を満たしていないAndroidManifestを持つライブラリやモジュールを探し、(外部ライブラリの場合は)exported属性を宣言されてるバージョンにアップデートすることで解決できるかと思います。

あと、今回の場合、ライブラリを定期的に更新していれば、こんな色々調べる羽目にはならなかったと思うので、ライブラリは定期的に更新しましょう！！


### 参考

https://stackoverflow.com/a/67668116/8800049