Skip to main content

Android 14 Migration Guide

As Android systems continue to evolve, Google Play has new requirements for the targetSdkVersion of apps.

As of August 31 2024, new apps and app updates must target Android 14 (API level 34) or higher to be submitted to Google Play.

Additionally, if more time is needed to upgrade an app, an extension can be requested until November 1, 2024. The extension form can be found from Google Play Console, usually in the app management page's inbox.

For more details, see Target API level requirements for Google Play apps.

This document aims to provide developers intending to upgrade the targetSdkVersion of their project to 34 with information regarding the changes and impacts of API level 34. Troubleshooting steps as well as their solutions for common impact points are also included, ensuring your app's smooth transition to Android 14.

note

To prevent compilation errors, both compileSdkVersion and targetSdkVersion has to be upgraded to 34.

Troubleshooting impact points in Android 14

Here are some updates and changes in Android 14 that could impact game developers. For a full rundown of everything new in Android 14, check out: https://developer.android.com/about/versions/14/summary

Restrictions on implicit and pending intents

For apps with targetSdkVersion=34, Android 14 has the following restrictions on implicit intents:

  • Implicit intents can only be sent to exported components. For components that have not been exported, explicit intents must be used, or the components must be specified as exported first.
  • If a mutable PendingIntent is created without specifying a component or package, the system will throw an exception.

Troubleshooting steps

Check if your game might be affected by unpacking the APK to see if the AndroidManifest contains component declarations that meet both of the below criteria:

  • Android component declaration includes the attribute android:exported="false".
  • Android component declaration includes an intent-filter with a custom action.

If you find any component declarations that meet both criteria, your app may be using implicit intents to launch components. Contact [diemliang] for further troubleshooting steps.

Sample code:

<service android:name="com.example.ExampleService"
android:exported="false">
<intent-filter>
<action android:name="com.example.EXAMPLE_ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>

Specify export behaviour for runtime-registered broadcast receivers

Broadcast receivers are often used to listen for changes in device status, such as network switches or changes in the battery level, and is a commonly used feature.

For apps with targetSdkVersion=34, any BroadcastReceiver registered at runtime through the Context must have its export attribute specified in the parameters, or the system will throw an exception.

Troubleshooting steps

Search the smali files to see if the following code snippet is present after unpacking the APK:

Landroid/content/Context;->registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;

If present, a portion of the logic involves registering a BroadcastReceiver using Context. Contact [diemliang] together with unpacked smali files for further troubleshooting steps.

note

For apps running on Unity 2020 or earlier versions, also see Unity.

Verify zip path traversal

For apps with targetSdkVersion=34, if the zip file entry name contains ".." or starts with "/", methods ZipFile(String) and ZipInputStream.getNextEntry() will throw a ZipException.

This is a safeguard against zip path traversal vulnerabilities.

Troubleshooting steps

Search the smali files to see if the following code snippets are present after unpacking the APK:

Ljava/util/zip/ZipFile;-><init>(Ljava/lang/String;)V
Ljava/util/zip/ZipInputStream;->getNextEntry()Ljava/util/zip/ZipEntry;

If present, it means your game involve using ZipFile and ZipInputStream. You'll need to check if the functionality using this interface runs smoothly on Android 14.
For the zip packages decompressed using the above interface, if the path of the compressed file contains ".." or starts with "/", it's advisable to alter the structure of the zip package and the way it's decompressed. Contact [diemliang] for further troubleshooting steps.

For apps with targetSdkVersion=34, user consent is required before every Capture Session. A single Capture Session can only be a one-time call to MediaProjection#createVirtualDisplay, and each MediaProjection instance can be used just once.

This API is related to screen recording, typically used to record standout player moves.

Troubleshooting steps

Search the smali files to see if the following code snippets are present after unpacking the APK:

Landroid/media/projection/MediaProjection;->createVirtualDisplay(
Landroid/media/projection/MediaProjectionManager;->createScreenCaptureIntent()

If present, it suggests your operations engage with MediaProjection. You'll need to check if the feature using this interface operates correctly on Android 14, particularly focusing on whether the feature functions normally when this interface is used multiple times in a single run. Contact [diemliang] for further troubleshooting steps.

Specify foreground service types

When opening other content in-game, such as Google login or WebView, the game app enters a background state and may get closed by the system. Adding permissions for foreground services to your app may help to circumvent this issue.

For apps that use foreground services with targetSdkVersion=34, at least one foreground service type must be specified for each foreground service. The foreground service type should represent your app's use case, in order to meet Android requirements.

The foreground service should also be declared in Google Play Console, from App content > Foreground service permissions. For example, if your app uses WebView, choose Other, then include a brief explanation together with a URL of a video of your app opening WebView in the game.

Image: GoogleService

For more information about the available types, see Foreground service types are required.

Troubleshooting steps

Search the smali files to see if the following code snippets are present after unpacking the APK, to determine if a foreground service is being used:

->startForeground(ILandroid/app/Notification;)V
->startForeground(ILandroid/app/Notification;I)V

If present, check the corresponding Service definition in AndroidManifest.xml to see if it contains the android:foregroundServiceType attribute and if the type is appropriate. Contact [diemliang] for further troubleshooting steps.

Sample code:

<!-- AndroidManifest.xml -->
<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Add the corresponding specific permission, in this example for mediaPlayback: FOREGROUND_SERVICE_MEDIA_PLAYBACK -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<application ...>
<!-- Define foregroundServiceType in the service -->
<service
android:name=".MyMediaPlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="false">
</service>
</application>
</manifest>

Changes in the project environment

In Unity 2020 or earlier versions, the registerReceiver method used in UnityPlayerActivity#onCreate does not specify exported components. This causes apps using Unity 2020 or earlier versions to malfunction on Android 14 when trying to upgrade targetSdkVersion to 34.

Upgrade Unity to the following LTS versions or later, which have fixed this issue:

  • 2021.3.33f1
  • 2022.3.13f1
  • 2023.1.20f1
  • 2023.3.0a11

For more information, see this bug report in the Unity Issue Tracker.

Image: MigrateToAndroid14/Unity2020deprecated

Upgrade Android Gradle Plugin (AGP)

According to Minimum versions of tools for Android API level from the official Google documentations, when your app has targetSdkVersion=34, the minimum required AGP version is 8.1.1.

However, in practical testing, it has been found that using version 7.1.2 is sufficient to meet the current compilation requirements. There is no need to upgrade to the higher version of 8.1.1.

  • For Unity 2022.2 or later, the default AGP version already meets the requirements, and no further action is necessary.
  • For Unity versions before Unity 2022.2, follow the below procedures to upgrade AGP.

Upgrade Gradle Wrapper version

  1. In the Editor, find the option Gradle installed with Unity by going to Unity > Settings > External Tools and uncheck it, then select the path to gradle-7.2-bin on your device for Gradle: Image: MigrateToAndroid14/GradleWrapper

  2. Modify the environment variable by setting GRADLE_HOME to the path of Gradle 7.2:

    # sh
    export GRADLE_HOME=/Users/***/.gradle/wrapper/dists/gradle-7.2-bin/****/gradle-7.2
    export PATH=${PATH}:${GRADLE_HOME}/bin

Modify AGP dependency version

note

If you have already upgraded to Player Network SDK V1.24, the AGP version in Assets/Plugins/Android/baseProjectTemplate.gradle has already been updated to 7.1.2, and does not need to be modified.

Check the version of "com.android.tools.build:gradle:x.x.x" in the Assets/Plugins/Android/baseProjectTemplate.gradle file. If the version is below 7.1.2, you will need to update it to 7.1.2.

Image: MigrateToAndroid14/AGP

Upgrade Java version

When upgrading the targetSdkVersion to 34, it's necessary to also upgrade the Java version depending on your Unity version:

  • For Unity 2022.3 or later, the default Java version already meets the requirements, and no further action is necessary.
  • For Unity versions before Unity 2022.3, follow the below procedures to upgrade Java.
  1. Navigate to Assets/Plugins/Android/gradleTemplate.properties and add the line org.gradle.java.home, specifying the value as the Java 11 path:

    org.gradle.java.home=/Users/***/Library/Java/jdk-11.0.22.jdk/Contents/Home
  2. Modify the environment variable by setting JAVA_HOME to the path of Java 11:

    # sh
    # set JAVA_HOME to your jdk 11 installation path.
    export JAVA_HOME=/Users/***/Library/Java/jdk-11.0.22.jdk/Contents/Home
    export PATH=${PATH}:${JAVA_HOME}/bin

Validation

Verify basic information

When unpacking an APK, check the following fields in the AndroidManifest.xml file to ensure they meet expectations:

  • Review the package attribute in the <manifest> tag to confirm that the package name is correct.

  • Examine the platformBuildVersionCode attribute in the <manifest> tag, which should be set to 34.

  • Look at the android:minSdkVersion attribute within the <uses-sdk> tag. This should reflect the minimum Android API level supported by your game.

    Relationship between Android API Levels and Android Versions: https://apilevels.com/

  • Check the android:targetSdkVersion attribute within the <uses-sdk> tag, expected to be 34.

AndroidManifest.xml example:

<!---AndroidManifest-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
android:compileSdkVersion="34"
android:compileSdkVersionCodename="14"
package="your.package.name"
platformBuildVersionCode="34"
platformBuildVersionName="14">

<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="34" />
......

Compatibility testing

You'll need to perform compatibility testing using the generated APK, with a particular focus on how the APK performs on Android 14 devices. Pay special attention to scenarios that are related to the behavior changes outlined in Section 2.

Appendix

Guide to Unpacking an APK

To confirm whether the game involves any changes in the API, it is recommended to globally search by decompiling the APK package. The recommended tool for decompiling is apktool:

Execute the following command to decompile the APK:

apktool d your_apk_path.apk
  • After executing the command, the decompiled files will be located in the /your_apk_path directory:

Image: MigrateToAndroid14/ApktoolOutput

FAQ

Error when executing **File > Build And Run**

Error message:

FAILURE: Build failed with an exception.
* Where:
Build file '/Users/***/launcher/build.gradle' line: 1
* What went wrong:
A problem occurred evaluating project ':launcher'.
> Failed to apply plugin 'com.android.internal.application'.
> The option 'android.enableR8' is deprecated.
It was removed in version 7.0 of the Android Gradle plugin.
Please remove it from `gradle.properties`.

The enableR8 configuration is no longer supported in AGP 7.0 or later, as R8 is now enabled by default.

In Unity Editor, navigate to Edit > Project Settings > Player > Android > Publishing Settings > Minify, and ensure that the Use R8 checkbox is checked to resolve the issue with Build And Run.

Unity 2021 compile error in gradle mergeDex

Error message:

> Task :launcher:mergeDexDebug FAILED
AGPBI: {"kind":"error","text":"com.android.tools.r8.kotlin.H","sources":[{}],"tool":"D8"}

com.android.tools.r8.kotlin.H

Execution failed for task ':launcher:mergeDexDebug'.
> Could not resolve all files for configuration ':launcher:debugRuntimeClasspath'.
> Failed to transform play-services-measurement-api-21.5.0.aar (com.google.android.gms:play-services-measurement-api:21.5.0) to match attributes {artifactType=android-dex, asm-transformed-variant=NONE, dexing-enable-desugaring=true, dexing-is-debuggable=true, dexing-min-sdk=16, org.gradle.status=release, org.gradle.usage=java-runtime}.
> Execution failed for DexingWithClasspathTransform: /Users/diemliang/.gradle/caches/transforms-3/e44e9aeb559794f90081ea4f47c131bc/transformed/jetified-play-services-measurement-api-21.5.0-runtime.jar.
> Error while dexing.

This issue due to r8 being specified as 4.0.48 in Unity 2021, which is incompatible with AGP 7.1.2. The official Google documentation recommends using 3.1.51 of r8 with AGP 7.1.2.

Modify the r8 version to V3.1.51 in settingsTemplate.gradle:

dependencies {
// classpath("com.android.tools:r8:4.0.48")
classpath("com.android.tools:r8:3.1.51") // Change to 3.1.51
}

If you have already upgraded to Player Network SDK V1.24, the r8 version in Assets/Plugins/Android/settingsTemplate.gradle has already been modified to 3.1.51, and does not need to be modified.