Note
Disclaimer: Non-Goals
This sample app is designed for demonstration and educational purposes, as a blueprint for technical integration. It does not provide a production-ready anti-abuse strategy. While the sample app demonstrates best practices for token handling and server-side verification, it is not a substitute for a comprehensive security audit.
Developers should treat the Play Integrity API as one signal within a broader, multi-layered anti-abuse strategy tailored to their specific business risks.
Warning
Security Notice: Cleartext HTTP in Development
Across the Android client, interactions rely on a dynamically configured base URL
(BuildConfig.BASE_URL) that points to a local Node.js server using unencrypted HTTP
(e.g., http://localhost:3000 or http://10.0.2.2:3000). This is an intentional design choice
for this sample to minimize setup complexity and ensure frictionless onboarding, avoiding the
need to generate and configure self-signed SSL/TLS certificates locally.
This configuration is strictly for local deployment. When integrating these
concepts into your own production app, you should secure your network layer by
using secure HTTPS (https://) and avoid having cleartext permissions in your
network_security_config.xml.
To run the Sample App end-to-end, you must configure a Google Cloud project, register your app in the Google Play Console, and set up both the local Node.js server and the Android client.
- Node.js v18 or higher installed
- The latest version of Android Studio installed
- A Google Play Developer account
- A Google Cloud account
First, establish the connection between your Google Cloud project and your Google Play app entry.
- Open the Google Cloud Console and create a new project.
- Navigate to APIs & Services > Library, search for the Google Play Integrity API, and click Enable.
- Open the Google Play Console and create
a new app entry.
- Note: Choose your package name carefully. You will use this exact package name to configure both the Android client and the Node.js server later.
- In the Play Console left navigation menu, select Protected with Play.
- Click Get Started on the Play Integrity API card.
- Follow the on-screen instructions to link the Google Cloud project you created in step 1.
- Enable the population of the following
optional verdicts
within the same section:
MEETS_STRONG_INTEGRITYMEETS_BASIC_INTEGRITY- Device attributes
- App access risk
- Play Protect
Your local server needs credentials to securely communicate with Google Cloud.
- In the Google Cloud Console, navigate to IAM & Admin > Service Accounts.
- Click Create Service Account. (Default settings are fine; no special roles are required).
- Select your newly created service account, select the Keys tab, and click Add Key > Create new key.
- Select JSON as the key type and click Create to download the credentials file to your machine.
Clone the repository containing the sample code to your local machine.
git clone https://github.com/android/security-samples.git
cd security-samplesConfigure and run the Node.js backend.
- Navigate to the server directory:
cd PlayIntegrityAPI/node-server - Install the required dependencies:
npm install - Move the downloaded JSON credentials file from Step 2 into the root of the
node-serverdirectory. - Rename the file to
google-credentials.json- Note: This filename is listed in
.gitignoreto prevent accidental credential leaks
- Note: This filename is listed in
- Create a file named
.envin the root of thenode-serverdirectory and define the following variables:PACKAGE_NAME="com.your.package.name"# Use the package name of the Play Console app entry created in Step 1GOOGLE_CREDENTIALS_PATH="./google-credentials.json"
- Start the server:
node app.js
Configure the Android app to communicate with your local server and your specific Google Cloud project.
- Open the
PlayIntegrityAPI/android-clientdirectory using Android Studio - Open the
local.propertiesfile in the project root and add your Google Cloud project number:GCP_PROJECT_NUMBER=1234567890
- Open the app-level
build.gradle.ktsfile and update theapplicationIdto match the package name of the app entry you created in the Play Console. - Sync your project with Gradle files.
- Select the
physicalReleasevariant from the Build Variants tool window, opened via the Tool Window bar on the far left of the Android Studio interface - Generate a signed Android App Bundle (AAB) using Android Studio.
- In the Google Play Console, set up an internal testing track and upload your signed AAB as a new release.
- Once the release is processed, use the internal testing link provided in the Play Console to install the app onto your physical test device.
- To allow the app on your device to communicate with your local machine's
backend server, connect the device via USB and set up ADB reverse port
forwarding in your terminal (replace
<SERVER_PORT>with your Node server port, e.g., 3000, and<CLIENT_PORT>with the port number that the app tries to access, e.g. 3000):adb reverse tcp:<CLIENT_PORT> tcp:<SERVER_PORT>- Note: once the device is disconnected, you will need to run this command again the next time you need to test this flow
The Banking micro-app demonstrates how to securely parse HTTP requests, cryptographically validate Play Integrity tokens, and enforce business rules.
When a user attempts to submit a secure transfer, the app requests an integrity token. If the device fails the integrity checks (e.g., a compromised device or unlicensed app), a remediation dialog prompts the user to resolve the issue (such as installing from Google Play). Upon successful remediation, the app retries the transaction securely.
See the following files in android-client/feature/bank:
- Token preparation (warm-up) & Handling remediation: BankViewModel.kt
- Request hash generation & Network execution: SubmitSecureTransferUseCase.kt
See the following files in node-server/src/features/bank:
- Cryptographic validation & Error formatting: bank.controller.js
- Business logic enforcement: bank.policy.js
The Streaming micro-app demonstrates how to parse standard integrity tokens, enforce tiered access policies, and dynamically modify DASH XML manifests.
The user accesses video content, which requests an integrity token to determine their device's trust tier. Based on the returned token, the backend dynamically modifies the video manifest to serve either premium or restricted streams. A user on a verified device enjoys high-quality streaming, while an unrecognized environment receives degraded quality without outright blocking playback.
See the following files in android-client/feature/streaming:
- ExoPlayer Network Injection & Dynamic Tiers: StreamingViewModel.kt
- Request Hash Generation (Content Binding): GetSecureStreamingConfigUseCase.kt
See the following files in node-server/src/features/streaming:
- Token Decoding, Replay Protection & Content Binding: streaming.controller.js
- Tiered Access Policies: streaming.policy.js
- Dynamic DASH XML Filtering: manifest.service.js
The Game micro-app showcases a stateful, secure verification pattern designed to defeat TOCTOU (Time-of-Check to Time-of-Use) cheats, enforce strict environment policies, and securely evaluate background Play Integrity API attestations.
Upon initiating a game session, a secure state is established on the server. While playing, the app performs background checks and sends periodic updates. If an anomaly is detected (like an attached debugger or a compromised environment), the game pauses and prompts the user for remediation. Once the environment is secure again, gameplay resumes, culminating in a securely validated final score submission.
See the following files in android-client/feature/game:
- Session Initialization: InitiateGameUseCase.kt
- TOCTOU Defence (Background Intervals) & Remediation: GameViewModel.kt
- Session Stop & Final Submission: SubmitGameScoreUseCase.kt
See the following files in node-server/src/features/game:
- Stateful Verification & TOCTOU Prevention: game.controller.js
- Environment Policy Rules: game.policy.js
This section guides you through using the Play Integrity API test responses feature in the Google Play Console to dynamically alter the streaming quality in the Streaming micro-app. This assumes you have already completed the full end-to-end setup as described in the root project guide (i.e. app created in Play Console, Play Integrity API enabled, Google Cloud project linked, Node.js server running, Android app buildable).
- A Google Play Developer account.
- Your app is set up in the Play Console.
- Play Integrity API is enabled for your app and linked to your Google Cloud project.
- The sample Node.js backend server is running.
- The Android client app is installed and runnable on a device or emulator, signed in with a Google account.
- Navigate to Play Integrity API Settings:
- Open the Google Play Console.
- Select your application.
- In the Play Console left navigation menu, select Protected with Play.
- On the Protected with Play page, locate the Play Integrity API row and click Manage.
- Configure Test Responses:
- Scroll down to the Testing section.
- Click Create new test.
- Give your test a descriptive name (e.g., "Device Unrecognized Test").
- Under Email lists, select or create an email list containing the Google account(s) used on your test device(s).
- Modify the Integrity verdicts to simulate different scenarios. For
example:
- Premium Quality (Fully Trusted):
appRecognitionVerdict: PLAY_RECOGNIZEDdeviceRecognitionVerdict: [MEETS_DEVICE_INTEGRITY, MEETS_STRONG_INTEGRITY]appLicensingVerdict: LICENSED
- Basic Quality (Basic Integrity):
appRecognitionVerdict: PLAY_RECOGNIZEDdeviceRecognitionVerdict: [MEETS_BASIC_INTEGRITY]appLicensingVerdict: LICENSED
- Restricted Quality (No Device Integrity):
appRecognitionVerdict: UNEVALUATEDdeviceRecognitionVerdict: [] (Empty)appLicensingVerdict: UNEVALUATED
- Premium Quality (Fully Trusted):
- Save the Test Configurations:
- Click Create test. You might need to click Save changes at the bottom of the page too. Propagation time varies depending on multiple factors, but changes should be reflected in about 1-2 hours at the longest.
- Observe in the Android App:
- Open the sample app on your test device and navigate to the Streaming micro-app (ensuring the device is signed in with one of the accounts from the email list in the test configuration).
- The app might show a quality level based on a previous integrity check.
- Click the Refresh Integrity Check button within the app. This action forces the app to request a new Play Integrity token and DASH manifest.
- Play Integrity API will return a token with the verdicts you configured in the Play Console test.
- The Node.js server will decode this test token and return a DASH manifest filtered according to the tier mapped to the received verdicts.
- Observe the UI: The highlighted tier card ("Premium", "Standard", or "Restricted") should update, and the video playback quality will adjust after ExoPlayer reloads the manifest.
- Simulate a Rooted/Compromised Device: Set
deviceRecognitionVerdictto be empty. The stream should degrade to the "Restricted" tier. - Simulate an Unlicensed User: Set
appLicensingVerdicttoUNLICENSED. The stream should also degrade to the "Restricted" tier. - Simulate a Fully Trusted Device & Licensed User: Ensure verdicts are
MEETS_STRONG_INTEGRITY,PLAY_RECOGNIZED, andLICENSED. The stream should allow "Premium" quality.
By changing the test responses in the Play Console and using the "Refresh Integrity Check" button, you can effectively test how the end-to-end integration handles various Play Integrity API outcomes and confirm that the stream quality adjusts dynamically as expected.
If you encounter issues or have feedback specific to this sample app, please file an issue in this repository.




