rea-reverse

Google Play App Rejection Reasons You Can Catch Before Upload

Written for REA 6.4.0Updated 10 min read
On this page

Short answer: Many Google Play rejections have nothing to do with screenshots or descriptions. They come from the release file itself: a targetSdkVersion below the current requirement, 32-bit native code without a 64-bit copy, a debuggable build, a debug signing certificate, or a restricted permission such as READ_SMS that nobody filed a declaration for. Every one of those can be read from the APK or AAB before you upload it, and fixed in your own build.

This guide builds a small App Bundle with the most common file-level problems, runs it through this site’s Google Play compliance checker, fixes each finding and runs it again. All output below is from those runs on 2026-10-14. The checker runs in your browser and does not upload the file.

Who this is for: Android developers who got “Your app is not compliant with Google Play policies” or a rejected update in Play Console, or who want to catch the problem before submitting.

What you will learn:

  • Which rejection reasons sit in the file and which sit in Play Console
  • What each check (rule ID, severity, condition) looks for, and the fix
  • How to find which library added a permission
  • How to resubmit or appeal after a rejection

Only analyze apps you build or are authorized to test.

Two kinds of rejection

  1. Store listing and declarations: privacy policy, Data safety form, content rating, ads declaration, sign-in details for reviewers. No file inspection can see these; Google’s Prepare your app for review page lists them.
  2. What the release file declares: target API level, ABIs, manifest flags, permissions, services, signing. These are facts in the APK or AAB, and this guide is about them.

Step 1: Check the bundle before you upload it

I built before.aab with aapt2, bundletool 1.18.3 and NDK r27, then signed it with jarsigner, the way an AAB is signed. It has the faults I see most often in real projects:

  • package com.example.rejectdemo, targetSdkVersion 35
  • android:debuggable="true" on the application
  • a native library in lib/armeabi-v7a/ only
  • READ_SMS, QUERY_ALL_PACKAGES, READ_MEDIA_IMAGES and FOREGROUND_SERVICE
  • a service with foregroundServiceType="dataSync" but no FOREGROUND_SERVICE_DATA_SYNC
  • signed with a key whose subject is CN=Android Debug,O=Android,C=US, the subject of the Android SDK’s debug keystore

Open the compliance checker (the Google Play scenario is pre-selected) and drop the file. The failed checks, as the analyzer core reported them:

PLAY-01 high    targetSdkVersion=35
                required: >= 36 (phone/tablet, since 2026-08-31)
                note: An extension to 2026-11-01 can be requested in Play Console;
                      without one, submissions are rejected.
PLAY-04 reject  lib/armeabi-v7a/ present, lib/arm64-v8a/ missing
PLAY-06 reject  application android:debuggable="true"
PLAY-07 reject  package="com.example.rejectdemo" (reserved prefix com.example.*)
PLAY-08 reject  uses-permission android.permission.READ_SMS
PLAY-09 high    uses-permission android.permission.QUERY_ALL_PACKAGES
                <queries>: none
PLAY-16 reject  uses-permission android.permission.READ_MEDIA_IMAGES
                targetSdkVersion=35
SIGN-01 reject  v1 signer: CN=Android Debug,O=Android,C=US — Android SDK debug certificate
                (SHA-256 575e39d821c4349d0e7699bdac8a01192e93d38d11506cc9aa34948c0fbf83d4)
PLAY-15 reject  service com.example.rejectdemo.SyncService (dataSync): missing FOREGROUND_SERVICE_DATA_SYNC
                targetSdkVersion=35

The report groups these as Will be rejected, High risk and Advice. I cross-checked the facts with the SDK tools: bundletool dump manifest listed the same four permissions, keytool -printcert -jarfile before.aab printed the same SHA-256, and llvm-readelf -lW showed the r27 arm64 library’s LOAD segments aligned to 0x1000.

Checked against the rules in force on 2026-11-01 (the date picker in the checker), the same file reports PLAY-01 as reject: the extension window has closed.

Step 2: Fix the reasons in the file

Each section below gives the rule ID, its severity and the condition exactly as the tool defines them, then the fix on your side.

Target API level (PLAY-01)

Reject. For phones and tablets, targetSdk must be at least 36 from 2026-08-31. While an extension to 2026-11-01 is still possible, a build at 35 is reported as high instead. Wear OS and Automotive need 35, TV and XR 34; the tool picks the form factor from required <uses-feature> entries.

Google’s target API level page states: “New apps and app updates must target Android 16 (API level 36) or higher to be submitted to Google Play”, with an extension to November 1, 2026 on request. Raise targetSdk, then test against that version’s behavior changes; that testing is the real work.

64-bit and 16 KB native libraries (PLAY-04, PLAY-02, PLAY-03)

PLAY-04, reject: armeabi-v7a/armeabi without arm64-v8a, or x86 without x86_64, or an ELF whose class or machine does not match its ABI folder. Google’s 64-bit requirement says that “for each native 32-bit architecture you support you must include the corresponding 64-bit architecture”. Add the ABI to abiFilters and get 64-bit builds of every prebuilt .so.

PLAY-02 (any PT_LOAD with p_align < 0x4000 in a 64-bit library) and PLAY-03 (an uncompressed 64-bit .so whose ZIP offset is not a multiple of 16384, APK only) are high until 2027-02-01 and reject from then on. Google’s 16 KB page size page says that from February 1, 2027, updates that don’t support 16 KB pages can’t be released. NDK r28 and newer align to 16 KB by default. The full walk-through is in Check an APK for Android’s 16 KB page size requirement.

Release build, package name and signing (PLAY-06, PLAY-07, SIGN-01, SIGN-02)

  • PLAY-06, reject: android:debuggable="true" or android:testOnly="true" on <application>. Build the release build type and delete any of these you added by hand.
  • PLAY-07, reject: a package name under com.example, com.android or android. Google’s application ID page uses com.example.myapp only as a placeholder and does not list reserved prefixes. Play Console refusing com.example uploads is widely reported by developers, so treat it as a hard stop. Pick a reverse-domain ID you own now: the same page warns that Play treats a changed ID as a different app.
  • SIGN-01, reject on Play: any signer certificate with CN=Android Debug. Google’s Sign your app page says that most app stores, “including the Google Play Store”, do not accept apps signed with a debug certificate.
  • SIGN-02, reject on Play: a signer whose SHA-256 matches a key whose private half is public, such as the AOSP test keys or the React Native template’s debug.keystore. The React Native template’s release build type uses that key until you configure your own.

Create an upload key valid for at least 25 years (the same Google page says Play requires a key validity ending after 22 October 2033), and enroll in Play App Signing. SIGN-04 (RSA under 2048 bits, DSA or MD5: high) and SIGN-05 (certificate expiring before 2033-10-22: reject for an APK, advice for an AAB, because an AAB carries the upload key) cover the rest.

App Bundle instead of APK (PLAY-05)

If you drop the universal APK from the same build into the checker, it adds PLAY-05, high: “input is an APK”, with the note that “a new app on Google Play must be uploaded as an AAB”. It is a rejection only for Android TV apps, which Play no longer accepts as APK updates. Build with ./gradlew bundleRelease.

Permissions that need a declaration

A finding here does not mean the permission is forbidden. It means the permission is limited to specific core uses, and Play Console will ask for a declaration. These are the checks and the alternative each fix points to:

Rule Severity Fires on Narrower alternative
PLAY-08 reject any SMS or Call Log permission SMS Retriever API
PLAY-09 high QUERY_ALL_PACKAGES <queries> for the packages you need
PLAY-10 reject MANAGE_EXTERNAL_STORAGE (high if only requestLegacyExternalStorage="true") Storage Access Framework, MediaStore
PLAY-12 high REQUEST_INSTALL_PACKAGES Play In-App Updates for self-updates
PLAY-13 high ACCESS_BACKGROUND_LOCATION foreground location only
PLAY-14 high USE_EXACT_ALARM (advice for SCHEDULE_EXACT_ALARM alone) inexact alarms, WorkManager
PLAY-16 reject READ_MEDIA_IMAGES or READ_MEDIA_VIDEO, only when targetSdk ≥ 33 system Photo Picker
PLAY-17 advice (high when targetSdk ≥ 37) READ_CONTACTS Android Contact Picker
PLAY-11 high a service protected by BIND_ACCESSIBILITY_SERVICE declaration plus in-app disclosure

The policy pages are specific about who may keep each permission. For SMS and Call Log, apps “must be actively registered as the default SMS, Phone, or Assistant handler” (SMS and Call Log permissions). For photos and videos, only apps whose “core functionality revolves around broad access to Photos and Videos” qualify (Photo and video permissions). QUERY_ALL_PACKAGES is for apps such as device search, antivirus, file managers and browsers (QUERY_ALL_PACKAGES). Apps that don’t qualify must remove the permission. Apps that do qualify must file the declaration.

Two dates from Google’s policy deadlines table: from 2027-01-27, phone-call account verification no longer qualifies for READ_CALL_LOG, and apps without a need for broad contacts access must switch to the Contact Picker.

Foreground services (PLAY-15)

Only when targetSdk ≥ 34. Reject in every scenario: a service declares a foregroundServiceType without its FOREGROUND_SERVICE_<TYPE> permission, because it crashes when started. High: FOREGROUND_SERVICE without any typed service, and on Play every typed foreground service (it needs a Play Console declaration) and USE_FULL_SCREEN_INTENT.

Google’s foreground service requirements ask for “a new declaration on the App content page” for each type, with a description, the user impact and a video showing how the user triggers the feature. Full-screen intents are enabled by default only for calling and alarm apps.

Step 3: Find where an unwanted permission came from

Often you never typed READ_SMS. A library merged it in. Android Studio’s Merged Manifest tab on AndroidManifest.xml shows the source of every element, and the build writes the full log to build/outputs/logs/manifest-merger-<buildVariant>-report.txt (Merge multiple manifest files). If the library’s feature that needs the permission is one you don’t use, remove the permission from your app’s manifest:

<uses-permission android:name="android.permission.READ_SMS" tools:node="remove" />

Removing a permission a library really uses can break that feature, so test it, or replace the library.

Step 4: Run it again

after.aab uses dev.example.rejectdemo, targetSdk 36, a release upload key (CN=Example Upload), NDK r28 builds for armeabi-v7a and arm64-v8a, no debuggable, no SMS, package query or media permissions, and adds FOREGROUND_SERVICE_DATA_SYNC. One finding remains:

PLAY-15 high    service dev.example.rejectdemo.SyncService: foregroundServiceType=dataSync (Play Console declaration)
                targetSdkVersion=36

That is expected: the file is fixed, and what remains is the foreground service declaration you file in Play Console.

What the file cannot tell you

A clean report covers the release file only. Keep a checklist for the rest:

  • Data safety form that matches every SDK in the app
  • Privacy policy link, and account deletion if users can create accounts
  • Sign-in details for reviewers if any part of the app is behind a login
  • Store listing that does not mislead or impersonate
  • For personal developer accounts created after November 13, 2023: a closed test with at least 12 testers opted in for 14 days before production (testing requirements)

Play Console also runs its own pre-review checks on the Publishing overview page, covering missing declarations, crash rates and possible policy problems. Google notes that passing them does not guarantee approval either. The checker’s results do not represent Google’s review decision.

After a rejection: Policy status, resubmit, appeal

  1. Read the reason. In Play Console, select the app and open Policy status in the left menu (Check your app’s policy status). For a rejection, “the last version that you successfully published is still available on Google Play”. The email names the policy.
  2. Fix and resubmit. Google’s resubmission steps say to “update all release types in addition to your production release”, which includes the internal, closed and open test tracks, and to deactivate the non-compliant bundle: “If you fail to deactivate the non-compliant app bundle(s), your attempt to resubmit your app will fail.”
  3. Appeal only if you believe the decision is wrong. The same page allows “one appeal per app removal, suspension, or other enforcement action”. If the file triggered the rule, fix it instead. An appeal will not change what the manifest says.
  4. Allow time. Google says some reviews take “up to seven days or longer in exceptional cases” (Publish your app).

A note on packers and hardening

If your app uses a commercial protection runtime, the checker reports PROT-01 as advice. Packing and hardening are legitimate; the finding only explains the trade-offs. Packer start-up code can look like a malware packer to heuristic scanners. Protections that verify their own signature can break under Play App Signing, because Google re-signs the app. Choose a solution that documents Play App Signing support, keep the same signing certificate, and never download executable code from outside Google Play.

Where REA fits

REA, the open-source project by morluto, has no Google Play policy checks in version 6.4.0. Its inspect-android-package command reads an APK’s package, SDK levels and permissions through a JADX engine you supply, and its contract says it “does not execute the APK or verify signatures”. It expects an APK, so convert an AAB with bundletool first (how). Its JADX class search helps you find the code behind a permission, which a declaration asks you to explain. Setup: JADX MCP server with Claude Code.

FAQ

Does a clean report mean Play will approve my app? No. It means no known file-level rule fired. Declarations, the Data safety form, the listing and runtime behavior are reviewed separately.

My app was rejected for a permission I removed. Why? Usually an older bundle that still requests it is active on a test track. Deactivate it on every track, as Google’s resubmission steps describe.

Can I keep a restricted permission? Yes, if your app’s core use qualifies. File the declaration in Play Console and be ready to show the feature in a video. The checker still reports it, because the review is still coming.

Next steps