【UE4】Google Play の要件を満たす App Bundle を作る

# 概要

UE 4.27 で Google Play の対象 API レベル要件や、それに付随する様々な要件を満たす Android App Bundle (AAB) を、できるだけ簡単に作る方法について解説する。
なお、署名や Play Asset Delivery (PAD) といった基本的な設定は完了している前提とする。

:::note info
対象 API レベル 34 で問題ないことは確認済み（2024 年 8 月時点）。
:::

# 環境

* Unreal Engine 4.27
  
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239989/5d1fadaf-1ec4-b4c7-6234-26d91083a0f6.png)
  
  <table>
      <tr>
          <td>Location of Android SDK</td>
          <td>C:/Users/[UserName]/AppData/Local/Android/Sdk</td>
      </tr>
      <tr>
          <td>Location of Android NDK</td>
          <td>C:/Users/[UserName]/AppData/Local/Android/Sdk/ndk/21.4.7075529</td>
      </tr>
      <tr>
          <td>Location of JAVA</td>
          <td>C:/Program Files/Android/Android Studio/jre</td>
      </tr>
      <tr>
          <td>SDK API Level</td>
          <td>latest</td>
      </tr>
      <tr>
          <td>NDK API Level</td>
          <td>android-21</td>
      </tr>
  </table><br>
  
* Android Studio 4.0.2
    * Android SDK Platform 29
    * Android SDK Build-Tools 29.0.2
    * NDK 21.4.7075529 (r21e)
    * Android SDK Command-line Tools (latest)
    * CMake 3.10.2.4988404
    * Android SDK Platform-Tools

:::note warn
各バージョンは厳密に合わせる必要がある。
:::

# 方法

以下の 3 点を行って AAB をパッケージングするだけ。

* 上記環境を構築
* プロジェクト設定の Target SDK Version に要件以上の値を設定
* 以下の GooglePlayReady プラグインをインストール

https://github.com/suramaru517/GooglePlayReady

# 解説

GooglePlayReady プラグインでは、以下の 3 点を行うことで Google Play の要件に対応している。

## 1. android:exported の追加

普通に AAB をアップロードすると、以下のエラーにひっかかる。

> アップロードされた APK または Android App Bundle には、インテント フィルタを使用するアクティビティ、アクティビティ エイリアス、サービス、またはブロードキャスト レシーバが含まれていますが、'android:exported' プロパティ セットは使用されていません。このファイルは Android 12 以降にはインストールできません。[詳細](https://developer.android.com/about/versions/12/behavior-changes-12#exported)

プラグインではこれを解決するため、Unreal Plugin Language (UPL) で Android Manifest のすべての activity, service, receiver タグに対して `android:exported` を追加している。

```GooglePlayReady_UPL.xml
<loopElements tag="activity">
	<setStringFromAttribute  result="ExportTag" tag="$" name="android:exported"/>
	<setBoolIsEqual result="ShouldNotAddExportTag" arg1="$S(ExportTag)" arg2="false"/>
	<if condition="ShouldNotAddExportTag">
		<false>
			<addAttribute tag="$" name="android:exported" value="true"/>
		</false>
	</if>
</loopElements>
...
```

## 2. com.android.vending.BILLING の削除

リリース画面に進むと、以下のエラーにひっかかる。

> このアプリで現在使用している Play Billing Library のバージョンは AIDL です。Google Play で最新の収益化機能を利用するには、バージョン 5.2.1 以上に更新する必要があります。[詳細](https://developer.android.com/google/play/billing/deprecation-faq)

アプリに課金要素がある場合はエラー文の通りに PBL を更新する必要があるが、ない場合は Billing の Permission を削除してしまえばよい。
プラグインでは、UPL で `com.android.vending.BILLING` を削除することで対処している。


```GooglePlayReady_UPL.xml
<removePermission android:name="com.android.vending.BILLING" />
```

なお Permission の削除については、以下の記事で解説している `ManifestRequirementsOverride.txt` を使う方法もある。

https://qiita.com/suramaru517/items/52a01d16e9a50f9f1d3e/

## 3. Play Core Library の更新

上記のエラーを解決しアプリをリリースできたとしても、PAD を使用している場合は Android 14 以降でアプリが起動しないという問題がある。
これを解決するには、以下のドキュメントに従って GooglePAD プラグインの APL を編集し、Play Core Library を更新する必要がある。

https://developer.android.com/guide/playcore?hl=ja#playcore-migration

```diff_groovy:Engine/Plugins/Runtime/GooglePAD/Source/GooglePAD/GooglePAD_APL.xml
- implementation('com.google.android.play:core:1.10.0')
+ implementation('com.google.android.play:asset-delivery:2.2.2')
+ implementation('com.google.android.play:asset-delivery-ktx:2.2.2')

- import com.google.android.play.core.tasks.OnCompleteListener;
- import com.google.android.play.core.tasks.OnSuccessListener;
- import com.google.android.play.core.tasks.RuntimeExecutionException;
- import com.google.android.play.core.tasks.Task;
+ import com.google.android.gms.tasks.OnCompleteListener;
+ import com.google.android.gms.tasks.OnSuccessListener;
+ import com.google.android.gms.tasks.RuntimeExecutionException;
+ import com.google.android.gms.tasks.Task;
```

またこれに伴い、`aar-imports.txt` や `app/build.gradle` も修正する必要がある。
なお、この修正は UE 4.27 Plus では既に適用されている。

```diff_kotlin:Engine/Build/Android/Java/aar-imports.txt
- com.google.android.gms,play-services-games,11.8.0
+ com.google.android.gms,play-services-games,18.0.1
```

```diff_gradle:Engine/Build/Android/Java/gradle/app/build.gradle
+ compileOptions {
+     sourceCompatibility JavaVersion.VERSION_1_8
+     targetCompatibility JavaVersion.VERSION_1_8
+ }

sourceSets.main {
```

これらはエンジンのファイルに変更を加えることになるが、エンジンソースを変更しているわけではないので、ソースからエンジンをビルドする必要はない。
プラグインでは、`GooglePlayReady.Build.cs` でこれらの変更を自動的に行っている。

# おまけ

Google Play にアップロードした Shipping の AAB でのみ発生する問題がある場合は、以下のコードを `[ProjectName].Target.cs` に追加して Shipping でのログを有効化してみるとよい。
ただし、これはソースからビルドしたエンジンでしか利用できない。

```[ProjectDirectory]/Source/[ProjectName].Target.cs
if (Configuration == UnrealTargetConfiguration.Shipping)
{
	bUseLoggingInShipping = true;
}
```

ログはここに保存される。
`Android/data/[PackageName]/files/UE4Game/[ProjectName]/[ProjectName]/Saved/Logs/[ProjectName].log`

なお Android 14 以降では `Android/data` 以下へのアクセス権が厳しくなったらしく、エクスプローラーからログを開けないという問題がある。
これは `AndroidPlatformFile.cpp` を以下のように編集し、ログ出力先のディレクトリを変更することで解決できる。

```cpp:Engine/Source/Runtime/Core/Private/Android/AndroidPlatformFile.cpp(1154)
//AndroidLogDir = LogBaseDir + GAndroidProjectName + TEXT("/") + GAndroidProjectName + TEXT("/Saved/Logs/");
AndroidLogDir = GExternalFilePath.Replace(TEXT("/Android/data/"), TEXT("/Documents/")) + TEXT("/Logs/");
```