Notification

You can now request help from the Help page in your Play Console account.  If you don't have access to Play Console, ask your account admin for an invite.

Customize anti-tamper protection

Note: This feature is only available to select Play partners.

This article describes how to customize Play anti-tamper protection for your apps and games. To use the customization features described on this page, you must have automatic protection with anti-tamper protection turned on in the Play Console.

About customized anti-tamper protection

Customized anti-tamper protection allows you to identify specific Java and Kotlin methods within your app that require stronger security. When your release is uploaded to Play, Google Play prioritizes enhanced protection for these specific methods.

By identifying methods for enhanced protection, you can improve your app's defense against tampering. You can also use method protection to guard against the exfiltration of client secrets from your binary (for example, sensitive keys or URLs that must be included in the client).

Set up customized anti-tamper protection

Follow these steps to implement customized anti-tamper protection in your app's codebase.

Step 1: Add the automatic protection interface to your codebase

First, add the protection interface to your app or game.

Kotlin:

package com.google.android.play.protections.annotations

/**
* Describes that a method is a candidate for enhanced protection.
*
* <p>This annotation will be removed automatically from the dex code of your
* bundle at the time when Google Play protects your bundle. Only use it as
* a method level annotation. Any other usage (e.g. accessing the class
* reflectively at runtime) is unsupported.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
public annotation class PlayAutomaticIntegrityProtection() {}

Java:

package com.google.android.play.protections.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Describes that a method is a candidate for enhanced protection.
*
* <p>This annotation will be removed automatically from the dex code of your
* bundle at the time when Google Play protects your bundle. Only use it as
* a method level annotation. Any other usage (e.g. accessing the class
* reflectively at runtime) is unsupported.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PlayAutomaticIntegrityProtection {}

Important: If you use additional tooling to optimize your app (such as ProGuard or R8), you must configure it so the annotation isn't lost during optimization. For ProGuard, add the following configuration:

# Keep the annotation class and its members.
-keep class com.google.android.play.protections.annotations.PlayAutomaticIntegrityProtection* { *; }

# By default, ProGuard treats annotation attributes as optional, and removes
# them in the obfuscation step. This rule will ensure they are kept.
-keepattributes RuntimeVisibleAnnotations, AnnotationDefault

# Keep any methods with the annotation, but allow them to be obfuscated.
-keepclassmembers,allowobfuscation class * {
    @com.google.android.play.protections.annotations.PlayAutomaticIntegrityProtection *;
}

Step 2: Annotate methods for custom protection

Once the interface is included in your app, you can add protection to specific methods by annotating them:
Kotlin:

import com.google.android.play.protections.annotations.PlayAutomaticIntegrityProtection

@PlayAutomaticIntegrityProtection()
fun myMethod() {
  // Method body...
}

 

Java

import com.google.android.play.protections.annotations.PlayAutomaticIntegrityProtection;

@PlayAutomaticIntegrityProtection()
public void myMethod() {
  // Method body...
}

Step 3: (Optional) Protect client-side secrets

Custom method protection obfuscates (hides) the code directly inside the annotated method body. You can use this to protect sensitive API keys.

Note: The API key string must be inlined into the protected method body (not extracted to a field), because only the direct body of the method will be obfuscated.

Kotlin:

import com.google.android.play.protections.annotations.PlayAutomaticIntegrityProtection

@PlayAutomaticIntegrityProtection()
fun callApi() {
  api.connect("YOUR_API_KEY")
}

 

Java:

import com.google.android.play.protections.annotations.PlayAutomaticIntegrityProtection;

@PlayAutomaticIntegrityProtection()
public static void callApi() {
  api.connect("YOUR_API_KEY");
}

Step 4: (Optional) Manage protection performance overhead

By default, calling a protected method adds an overhead of several milliseconds. Therefore, it must not be called in performance-sensitive scenarios or on the main thread.

If you need to protect a method where fast execution is necessary, a lighter protection mode is available. You can use an annotation attribute to opt for faster access (loaded at startup), though this corresponding protection is not as strong.

Kotlin:

@PlayAutomaticIntegrityProtection(loadAtStartup = true)
fun callApi() {
  api.connect("YOUR_API_KEY")
}

 

Java:

@PlayAutomaticIntegrityProtection(loadAtStartup = true)
public static void callApi() {
  api.connect("YOUR_API_KEY");
}

To use this, add the interface attribute to the body of your original annotation definition from Step 1:

// Kotlin Annotation Definition Update
public annotation class PlayAutomaticIntegrityProtection(
  /**
  * If true, the annotated method will be loaded at startup. If false, it will
  * be loaded on demand.
  *
  * <p>For on demand loading, calling the method will incur a performance
  * penalty. For startup loading, the method will remain deobfuscated in memory
  * for the lifetime of the app.
  */
  val loadAtStartup: Boolean = false
) {}

Step 5: Test your app

Once you have protected your app or game through the Play Console, test it thoroughly. Verify that the annotated methods for enhanced protection are invoked during your testing so you can accurately assess any potential impact on app performance.

Best practices for selecting methods

A thoughtful selection of methods leads to stronger overall protection. If each new release has a newly protected method, that version of the app or game will be more resilient to attacks.

As a general rule to avoid impacting your app's performance, select cold methods rather than hot methods. For maximum protection, select methods that:

  • Are critical to the application (the app would fail if removed).
  • Are not executed in the main/UI thread.
  • Run more than once during the lifetime of the app, but don't run in a hot loop.
  • Are non-trivial (methods containing non-trivial code or data).
  • Are not executed during startup, if possible.
  • Are newly introduced or heavily refactored in the release.
  • Are not abstract methods, interface methods, or constructors.
  • Do not use reflection and do not directly load a native library.

Remove customized protection

If you no longer want to protect a specific method, you can simply remove the @PlayAutomaticIntegrityProtection() annotation.

Important: We do not recommend turning off protection once your protected artifact has been published to an open track, as this could facilitate diff-based attacks. When testing different artifacts with custom method protections turned on and off, always do so in your internal testing track.

 

Was this helpful?

How can we improve it?
Search
Clear search
Close search
Main menu
16342173968581452956
true
Search Help Center
false
true
true
true
true
true
92637
false
false
false
false
false