Reference
Every check the app report makes
In short
The app report reads an APK or app bundle in your browser and applies the 74 checks below. Each one says why Google Play cares and links to Google's own page; the rules were last compared with those pages on 17 September 2026. Every rule is readable code, so if one is wrong or missing, say so. The command line and the MCP server run the same checks, and publishly check --sarif names them by the ids shown here.
1. Google Play policy (17)
What Play Console refuses or asks about, from the target SDK to signing and crash-report files.
Targets an Android version below what Play requires
New apps and updates must target the Android version Play currently asks for. Before the deadline this is a warning; after it, Play refuses the upload.
Foreground services without a type
An app that targets Android 14 or later must declare a foregroundServiceType on every foreground service. Starting one without a type throws.
Uses the Accessibility APIs
Play only allows accessibility services that help people with disabilities. Automation, overlays and reading other apps are removed, and this is a common cause of rejection.
Advertising SDKs found
Ad SDKs read the advertising ID and app activity, which the Data safety form has to declare. Apps that target Android 13 or later also need the AD_ID permission.
Analytics SDKs found
Analytics SDKs collect app activity and device identifiers, which the Data safety form has to cover.
Sign-in without an account deletion route
Apps where people can create an account must offer a way to delete it, both inside the app and from a web link given in Play Console.
A first release from a personal account needs a closed test
Personal developer accounts created recently must run a closed test with a minimum number of testers for a set period before their first production release.
Developer verification
Google is starting to require every developer who distributes apps to certified Android devices to verify their identity, whether or not they publish on Play.
Not signed
Android won't install an unsigned APK, and Google Play won't accept an unsigned upload.
Signed with the debug key
The debug certificate is the same on every developer machine. Play refuses anything signed with it.
The signing certificate has expired
A certificate past its end date can't sign an update that installs over the version people already have.
Signing certificate expires too soon
Google Play requires the key an app is signed with to stay valid until after 22 October 2033, so users can keep updating it.
No native debug symbols in the bundle
Without symbols, native crashes in Play Console's Android vitals show addresses instead of function names.
Upload native debug symbols with this APK
An APK can't carry debug symbols for its native libraries, so they go up alongside it in Play Console.
No R8 mapping file in the bundle
The code is shrunk and renamed by R8, but the mapping is missing, so crash and ANR stack traces in Play Console stay obfuscated.
Upload the R8 mapping file with this APK
An APK doesn't carry the R8 mapping, so stack traces in Play Console stay obfuscated until it's uploaded for this version.
Play Billing Library below the version Play requires
A new app or an update that uses Play's billing system must use a supported major version of the Billing Library. Older versions keep working for what is already published, but no further update can be released.
2. Permissions (30)
Permissions Play restricts or asks a declaration for, and the narrower API that usually does the same job.
Asks for permissions only system apps can get
Signature and privileged permissions are refused for a normal app. They do nothing except alarm anyone reading the manifest.
Permissions that need a Play declaration
Each one is a form to fill in, a reason to be reviewed, and a reason for users to hesitate on the install screen.
Read all photos (READ_MEDIA_IMAGES)
Play only allows broad photo access when it is the app's core purpose; everything else must use the photo picker. Instead: The Android photo picker (PickVisualMedia), which needs no permission at all.
Read all videos (READ_MEDIA_VIDEO)
Same policy as photos: broad video access needs a declaration and a core use. Instead: The Android photo picker (PickVisualMedia).
Read shared storage (READ_EXTERNAL_STORAGE)
Ignored on Android 13 and later, and a broad ask on older ones. Instead: The photo picker for media, or the Storage Access Framework for documents.
Write to shared storage (WRITE_EXTERNAL_STORAGE)
Has no effect from Android 11 onwards, and Play treats it as sensitive on older devices. Instead: MediaStore for media, or the Storage Access Framework for files the user picks.
Access all files (MANAGE_EXTERNAL_STORAGE)
The widest storage access there is. Play allows it only for file managers, backup and antivirus apps, and asks for a declaration. Instead: The Storage Access Framework, or app-specific storage.
See every installed app (QUERY_ALL_PACKAGES)
Play allows it only when the app genuinely needs to know about all apps; it needs a declaration. Instead: A <queries> element naming the apps or intents you actually need.
Read text messages (READ_SMS)
Play restricts SMS access to a default SMS handler; using it for verification codes is not allowed. Instead: The SMS Retriever API, which reads only your own verification code and needs no permission.
Receive text messages (RECEIVE_SMS)
Same SMS restriction: only a default SMS handler may keep it. Instead: The SMS User Consent API.
Send text messages (SEND_SMS)
Restricted by Play, and it can cost the user money. Instead: Hand the message to the SMS app with an intent, so the user presses send.
Read the call log (READ_CALL_LOG)
Restricted to default phone and assistant apps. Instead: Usually nothing is needed; ask what the feature really requires.
See outgoing calls (PROCESS_OUTGOING_CALLS)
Removed in Android 10 and restricted by Play. Instead: CallScreeningService, for a call-screening app.
Location in the background (ACCESS_BACKGROUND_LOCATION)
Play allows it only when a feature genuinely needs location while the app is closed, and asks for a video showing it. Instead: Ask only for location while the app is in use.
Exact location (ACCESS_FINE_LOCATION)
Exact location is sensitive; many features work with an approximate one. Instead: ACCESS_COARSE_LOCATION, which is accurate to about a city block.
Install other apps (REQUEST_INSTALL_PACKAGES)
Play allows it only for app stores, file managers and similar; it is a common reason for rejection. Instead: Send the user to Play, or open the file with the system installer.
Draw over other apps (SYSTEM_ALERT_WINDOW)
Overlays can hide what the user is agreeing to, so Play looks at them closely. Instead: A notification, a bubble, or picture-in-picture.
Exact alarms (SCHEDULE_EXACT_ALARM)
From Android 14 this is only granted to alarm clocks and calendars; other apps get it denied. Instead: setWindow or WorkManager, or USE_EXACT_ALARM if the app really is an alarm clock.
Full-screen notifications (USE_FULL_SCREEN_INTENT)
From Android 14 only calling and alarm apps are granted this by default. Instead: A normal high-priority notification.
Read contacts (READ_CONTACTS)
Personal data that most apps only need one entry from. Instead: The contact picker intent, which returns just the contact the user chose.
See the device accounts (GET_ACCOUNTS)
Mostly replaced, and it exposes every account on the phone. Instead: Credential Manager or Sign in with Google.
Read phone state (READ_PHONE_STATE)
Often taken for a device id, which Play treats as personal data. Instead: The Phone Number Hint API for a number, or an app-generated id for analytics.
Camera (CAMERA)
Fine when the app has its own camera view, wasteful when it only wants one photo. Instead: ACTION_IMAGE_CAPTURE, which lets the camera app take the picture.
Microphone (RECORD_AUDIO)
Play asks that it only be used while a feature the user started is running.
App usage history (PACKAGE_USAGE_STATS)
Shows what the user does in every other app. Instead: Only digital-wellbeing style apps should ask; most features don't need it.
Accessibility service (BIND_ACCESSIBILITY_SERVICE)
Play only allows the Accessibility APIs when they help users with disabilities; anything else must use another API and will be removed. Instead: isAccessibilityTool must be set and the service must genuinely assist users.
Advertising ID (com.google.android.gms.permission.AD_ID)
Apps that target Android 13 or later must declare this to read the advertising ID, and disclose it in Data safety.
Notifications (POST_NOTIFICATIONS)
Normal for most apps; ask for it when the user does something that expects a notification.
Scan for Bluetooth devices (BLUETOOTH_SCAN)
Without the neverForLocation flag this drags location permission in with it. Instead: Add android:usesPermissionFlags="neverForLocation", or use Companion Device Manager.
Foreground service (FOREGROUND_SERVICE)
From Android 14 each foreground service also needs a type, and Play asks what it is for.
3. Security (14)
What anyone who opens the APK can see or reach.
Built as debuggable
android:debuggable lets anyone attach a debugger and read the app's data on any device. Play rejects debuggable uploads.
Allows unencrypted HTTP traffic
With usesCleartextTraffic on, the app may send data over plain HTTP, where anyone on the same network can read or change it.
Plain http:// addresses in the code
Traffic to these is unencrypted and can be read or changed on any shared network. Some may belong to a library rather than the app's own code.
Backups are on by default
App data is copied into the user's backup, which can include tokens and local databases, unless rules exclude them.
No backup rules for Android 12 and later
With backups on and a target of Android 12 or later, dataExtractionRules decides what goes into backups and phone-to-phone transfers. Without them, everything does.
Components open to other apps
Exported activities, services and receivers can be started by any app on the phone, so each one has to expect that.
Code can be loaded at runtime
Play's Device and Network Abuse policy forbids downloading executable code from outside the store. A class loader is fine for a plugin the app ships with, and worth a look otherwise.
A WebView can call back into the app
addJavascriptInterface gives web content a route into app code. It is safe only when the page loaded is one the app controls.
Google API key in the code
Google API keys inside an app are normal (Maps and Firebase need them), but anyone can read them out of the APK, so each one must be restricted to this app's package and signing certificate.
Google OAuth client secret in the code
Anyone who opens the APK can read it, so it is public from now on: revoke it, move the work to a server, and give the app a token it can't misuse.
AWS access key in the code
Anyone who opens the APK can read it, so it is public from now on: revoke it, move the work to a server, and give the app a token it can't misuse.
Stripe secret key in the code
Anyone who opens the APK can read it, so it is public from now on: revoke it, move the work to a server, and give the app a token it can't misuse.
Slack token in the code
Anyone who opens the APK can read it, so it is public from now on: revoke it, move the work to a server, and give the app a token it can't misuse.
Private key in the code
Anyone who opens the APK can read it, so it is public from now on: revoke it, move the work to a server, and give the app a token it can't misuse.
4. Compatibility (7)
What stops the app installing or running on some devices.
Lists installed apps without declaring which it needs
QUERY_ALL_PACKAGES needs a Play declaration and is rarely granted. A <queries> element naming the apps it actually needs to see is usually enough.
Native libraries built for 4 KB pages
Play requires apps with native code to support 16 KB page sizes. Libraries linked for 4 KB pages crash on 16 KB devices, and after the deadline Play refuses the upload.
Native libraries not 16 KB aligned in the APK
The libraries themselves are fine, but they don't start on a 16 KB boundary inside the package, so a 16 KB device still can't map them directly.
Ready for 16 KB page sizes
Every 64-bit native library is aligned for 16 KB pages, so the requirement is already met.
No 64-bit version of the native code
Play has required a 64-bit version alongside 32-bit libraries since 2019, and 64-bit-only devices can't run a 32-bit-only app at all.
Only the old v1 signature
An app that targets Android 11 or later must also be signed with APK Signature Scheme v2 or later, or devices on Android 11 and up refuse to install it.
Several dex files, but no multidex library
Below Android 5.0 only the first dex file loads unless the multidex support library is included, so the app crashes on start there.
5. Libraries (2)
Only with the opt-in library-version lookup, which sends library names to Google's Maven repository.
Libraries several major versions behind
Old SDK versions are where known security and Play policy problems live, and Play's SDK Index warns about some of them in Play Console. They also tend to block a target SDK upgrade.
Libraries well behind
Worth picking up at the next update; none are urgent on their own.
6. Reach (4)
What keeps the app out of search results or off some phones without breaking anything.
Permissions that imply required hardware
Some permissions make Play assume the hardware is required, which hides the app from devices without it, unless a uses-feature element marks it optional.
Orientation and resizing locks are ignored on large screens
From Android 16, on screens 600dp and wider, an app that targets API 36 can't lock its orientation or aspect ratio or refuse to resize. Games are exempt, and there is a one-version opt-out.
Hidden from new users on recent phones
Play only shows an app to new users whose phone runs an Android version no more than a few versions newer than the one the app targets.
Only in one language, or few
Most installs come from people browsing Play in their own language, and an untranslated app is skipped over.