AppLovin / AppLovin-MAX-Unity-Plugin

110 stars 31 forks source link

Critical Issue Allowing Players to Bypass Advertisements [ANDROID] #335

Closed agrobost closed 4 months ago

agrobost commented 4 months ago

MAX Plugin Version

6.1.2

Unity Version

2022.3.16f1

Device/Platform Info

Android

Current Behavior

Players can bypass advertisements in the mobile application. When an ad appears, they can press the home button to return to their device's main screen with all apps listed. Upon reopening the application by clicking its icon, the app resumes without displaying the previously shown advertisement.

Expected Behavior

Advertisements should not be skippable without completion or interaction as intended. Players should either watch the full ad or interact with it as per the app's design, ensuring consistent ad revenue generation.

How to Reproduce

  1. Launch the application integrated with the AppLovin-MAX-Unity-Plugin.
  2. Engage with the app until an advertisement is triggered.
  3. Press the home button to exit to the device's main screen.
  4. Reopen the application by clicking its icon.
  5. Observe that the advertisement is bypassed and no longer displayed.

Additional Info

This issue is critical as it affects the monetization strategy of the application, potentially leading to significant revenue loss. The ability to skip ads undermines the advertising model and impacts the financial viability of apps using the plugin. A prompt resolution is essential to maintain the integrity of the ad-based revenue model.

santoshbagadi commented 4 months ago

@agrobost thank you for bringing this to our attention. This is a known issue as mentioned here: https://github.com/AppLovin/AppLovin-MAX-Unity-Plugin/issues/315#issuecomment-1849315544

We are looking into ways to fix this on our end but in the mean time you can update the launchMode of the app to fix this issue.

agrobost commented 4 months ago

I attempted to fix this by updating the launchMode in the AndroidManifest.xml. However, Unity overrides any changes to this file during its build process.

I also explored solutions on various forums, like this Unity forum discussion, but unfortunately these didn't work

Are you aware of any workarounds or alternative strategies that could help in preventing this issue? This is a critical concern for us, as it impacts the app's ad revenue model significantly.

Thank you for your support.

agrobost commented 4 months ago

I found a workaround by modifying the launcherTemplate.gradle script:

// Import classes from the Groovy XML library
import groovy.xml.XmlUtil
import groovy.xml.XmlParser

// This section is for code generated by Unity in the launcherTemplate.
// ...

// End of Unity generated code.

// Iterate over all application variants. This is typical in Android build scripts where you might have different variants like debug, release, etc.
android.applicationVariants.all { variant ->
    // Iterate over each output of the variant. There could be multiple outputs for a variant.
    variant.outputs.each { output ->
        // Get the process manifest provider for the output and apply a closure in the last execution phase.
        def processManifest = output.getProcessManifestProvider().get()
        processManifest.doLast { task ->
            // Get the directory for the output manifest files.
            def outputDir = task.getMultiApkManifestOutputDirectory()
            File outputDirectory
            // Check the type of outputDir and assign it to outputDirectory.
            if (outputDir instanceof File) {
                outputDirectory = outputDir
            } else {
                outputDirectory = outputDir.get().asFile
            }

            // Define the path to the AndroidManifest.xml file in the output directory.
            File manifestOutFile = file("$outputDirectory/AndroidManifest.xml")

            // Parse the AndroidManifest.xml file using XmlSlurper, which is a lazy parser. Declare the namespace for Android.
            def manifest = new XmlSlurper(false, true)
                            .parse(manifestOutFile)
                            .declareNamespace(android: 'http://schemas.android.com/apk/res/android')
            // Get all activity nodes from the parsed manifest.
            def activities =  manifest.application.activity;
            // Iterate over each activity node.
            activities.each { activity ->
                // If the activity's name is UnityPlayerActivity, set its launchMode to 'standard'.
                if(activity.'@android:name' == "com.unity3d.player.UnityPlayerActivity")
                    activity.'@android:launchMode' = 'standard'
            }
            // Serialize the modified XML and write it back to the AndroidManifest.xml file.
            manifestOutFile.text = XmlUtil.serialize(manifest)
        }
    }
}