Most release blockers on Google Play can be read straight out of the file you are about to ship. The targetSdkVersion is in AndroidManifest.xml. Whether a native library is 16 KB aligned is written in its ELF program headers. Whether the build was signed with the debug key is written in the certificate. None of that needs a server, and none of it needs your source code.
This tool reads those fields from the finished APK or AAB, entirely inside your browser, and compares them with the Google Play and sideloading rules that most often stop a release. Every finding comes with the evidence it is based on, a fix, and a link to the official page that states the rule.
What it checks
It runs 47 checks: Google Play requirements, sideloading and install problems, signing, privacy and app protection. The IDs match the rule list used across this site, so a link such as #PLAY-02 in a report always means the same thing. The “When it fires” column gives the exact condition, for example that SIGN-03 only rejects when targetSdk is 30 or higher. If you only care about Google Play, the Google Play compliance checker is the same tool with the Play scenario pre-selected.
| ID | What is checked | Severity | When it fires | Applies to |
|---|---|---|---|---|
PLAY-01 | targetSdkVersion below the Google Play requirement | Will be rejected | Play only. Phones/tablets: targetSdk ≥ 36 from 2026-08-31 (high risk while an extension to 2026-11-01 is possible, if ≥ 35); Wear OS / Automotive ≥ 35; TV ≥ 34; XR ≥ 34. Form factor from required <uses-feature>. | Google Play |
PLAY-02 | 16 KB page size: ELF segments not 16 KB aligned | Will be rejected (from 2027-02-01) | Play only. .so files in lib/arm64-v8a and lib/x86_64 with any PT_LOAD p_align < 0x4000. High risk until 2027-02-01, reject from then. | Google Play |
PLAY-03 | 16 KB page size: uncompressed .so not 16 KB aligned in the ZIP | Will be rejected (from 2027-02-01) | Play, APK only. Uncompressed (STORED) 64-bit .so whose data offset is not a multiple of 16384; skipped when extractNativeLibs="true". High risk until 2027-02-01. | Google Play |
PLAY-04 | Missing 64-bit native libraries | Will be rejected | Play only. armeabi-v7a/armeabi without arm64-v8a, or x86 without x86_64; or an ELF whose class/machine does not match its ABI directory. | Google Play |
PLAY-06 | Debuggable or test-only build | Will be rejected | Play only. application android:debuggable="true" or android:testOnly="true". | Google Play |
PLAY-07 | Package name uses a reserved prefix | Will be rejected | Play only. Package name com.example.*, com.android.* or android.*. | Google Play |
PLAY-08 | SMS or Call Log permissions | Will be rejected | Play only. Any SMS or Call Log permission is requested. | Google Play |
PLAY-09 | QUERY_ALL_PACKAGES permission | High risk | Play only. QUERY_ALL_PACKAGES is requested (high risk: declaration review). | Google Play |
PLAY-10 | All files access | Will be rejected | Play only. MANAGE_EXTERNAL_STORAGE → reject; requestLegacyExternalStorage="true" alone → high risk. | Google Play |
PLAY-13 | Background location permission | High risk | Play only. ACCESS_BACKGROUND_LOCATION is requested. | Google Play |
PLAY-16 | Photo and video permissions | Will be rejected | Play only, and only when targetSdk ≥ 33: READ_MEDIA_IMAGES or READ_MEDIA_VIDEO is requested. | Google Play |
SIDE-01 | targetSdkVersion too low to install on new Android versions | Will be rejected | Outside Play. targetSdk < 24 (Android 15+ refuses to install) or < 23 (Android 14+). | Outside Play (sideload) |
SIGN-01 | Signed with an Android debug certificate | Will be rejected | Any signer certificate whose subject CN is "Android Debug" (the SDK debug keystore is CN=Android Debug, O=Android, C=US; framework templates such as React Native use other O values). Reject on Play; high risk outside Play. Google states that Play does not accept apps signed with a debug certificate; that Play identifies them by this subject is likely but not documented. | Google Play / Outside Play (sideload) |
SIGN-03 | Only a v1 (JAR) signature | Will be rejected | APK only. No v2/v3 signature: reject only when targetSdk ≥ 30 (cannot install), high risk below 30; no signature at all is reported separately. | Google Play / Outside Play (sideload) |
PLAY-05 | An APK instead of an Android App Bundle for Google Play | High risk | Play scenario, APK files only. High risk: new apps must be published as an AAB (since August 2021); Google’s pages state the requirement for new apps. Reject for Android TV apps (required <uses-feature android.software.leanback>): Play no longer accepts APK updates for TV apps since 2023-06-30. | Google Play |
PLAY-11 | Accessibility service declared | High risk | Play only. Any <service> protected by BIND_ACCESSIBILITY_SERVICE. Its android:isAccessibilityTool value is read from the service config XML when the resource table resolves it. | Google Play |
PLAY-14 | Exact alarm permission | High risk | Play only. USE_EXACT_ALARM → high risk (only for apps whose core function is an alarm clock or calendar). SCHEDULE_EXACT_ALARM alone → advice (users can deny it). | Google Play |
PLAY-15 | Foreground service types and full-screen intents | High risk | Only when targetSdk ≥ 34. Reject (everywhere): a service declares a foregroundServiceType without its FOREGROUND_SERVICE_<TYPE> permission (it crashes when started). High risk: FOREGROUND_SERVICE without any typed service; on Play also every typed foreground service (Play Console declaration) and USE_FULL_SCREEN_INTENT. Which untyped services are started in the foreground is not traced (that needs data flow through the code). | Google Play / Outside Play (sideload) |
PLAY-17 | Contacts permission (new Play rule) | Advice | Play only. READ_CONTACTS is requested: advice; high risk when targetSdk ≥ 37 (the new minimum-scope rule applies to apps targeting Android 17, enforced from 2027-01-27). | Google Play |
SIDE-02 | Old targetSdk: Play Protect warning on install | High risk | Outside Play. targetSdk more than two API levels below the latest Android release on the check date (the latest level is kept in the rule data). | Outside Play (sideload) |
SIDE-03 | Permissions blocked for sideloaded apps by enhanced fraud protection | High risk | Outside Play. RECEIVE_SMS, READ_SMS, a notification listener or an accessibility service. In the select markets where Google Play Protect’s enhanced fraud protection is active (Google does not publish a full list), installs of such apps from browsers, messaging apps or file managers are blocked. | Outside Play (sideload) |
SIDE-04 | Accessibility service marked as an accessibility tool | High risk | Outside Play. An accessibility service whose config sets isAccessibilityTool="true". Only a hint: whether the app really is a tool for people with disabilities needs a human check. Unknown when the config XML cannot be read. | Outside Play (sideload) |
SIDE-06 | High-risk combination of capabilities | High risk | Outside Play. Three or more of: SYSTEM_ALERT_WINDOW, an accessibility service, SMS permissions, a device admin receiver, a notification listener. | Outside Play (sideload) |
SIDE-07 | No launcher entry | Advice | Outside Play. No activity or activity-alias with MAIN + LAUNCHER (or LEANBACK_LAUNCHER), and no keyboard, wallpaper, accessibility, dream, TV input, home-screen or other system-style component. | Outside Play (sideload) |
SIDE-08 | Split APK shipped as if it were a complete app | Will be rejected | Outside Play, APK only. Reject (cannot install alone): the file is a split (<manifest split>, isFeatureSplit), which fails with INSTALL_FAILED_INVALID_APK "Full install must include a base package"; or a base APK whose <manifest> sets android:isSplitRequired="true" or android:requiredSplitTypes, which fails with INSTALL_FAILED_MISSING_SPLIT. High risk: a base APK flagged only with <application android:isSplitRequired> or the meta-data com.android.vending.splits.required. The installer does not read those, so it may install but is incomplete without its splits (code or resources from the splits can be missing at runtime). | Outside Play (sideload) |
SIDE-09 | Non-standard ZIP structure | High risk | APK only. Duplicate entries, central directory / local header mismatches, unsafe paths, the encryption flag or an unknown compression method → high risk; extra bytes after the archive → advice. | Google Play / Outside Play (sideload) |
SIDE-10 | Android developer verification: what to register | Advice | Outside Play, APK only. Always lists the package name and signing certificate SHA-256 to register. High risk when the signer is the debug certificate or a public AOSP test key, which cannot prove ownership. | Outside Play (sideload) |
SIGN-02 | Signed with a publicly known key | Will be rejected | A signer certificate matches, by SHA-256, a key whose private half is public: the AOSP test certificates (testkey, platform, shared, media, …) or the React Native template’s debug.keystore (also in Expo’s bare template). Reject on Play; high risk outside Play. | Google Play / Outside Play (sideload) |
SIGN-04 | Weak signing key or certificate digest | High risk | RSA key under 2048 bits, a DSA key, or an MD5-based certificate signature → high risk; a SHA-1-based certificate signature → advice. | Google Play / Outside Play (sideload) |
SIGN-05 | Signing certificate expires before 22 October 2033 | Will be rejected | Play only. A signer certificate’s notAfter is before 2033-10-22. APK: reject (the rule applies to the app signing key). AAB: advice only (the AAB carries your upload key; Google signs the app with the app signing key, and the 2033 rule is stated for that key). | Google Play |
SIGN-08 | ZIP alignment and resources.arsc storage | Will be rejected | APK only. Reject only when targetSdk ≥ 30 and resources.arsc is compressed or not 4-byte aligned (cannot install). Other uncompressed entries not 4-byte aligned → advice. | Google Play / Outside Play (sideload) |
PRIV-07 | Cleartext traffic or user-installed CAs trusted | Advice | usesCleartextTraffic="true"; or not declared with targetSdk < 28 (cleartext then allowed by default); or the network security config permits cleartext or trusts user CAs outside <debug-overrides>. Unknown when the config cannot be read and nothing else is found. | Google Play / Outside Play (sideload) |
PROT-01 | App protection (packer / hardening) identified | Advice | Identification only: a commercial app-protection runtime by its library file name, or (weak signal) one very small classes.dex next to large assets. Protecting code is legitimate; this only explains the trade-offs. | Google Play / Outside Play (sideload) |
PLAY-12 | REQUEST_INSTALL_PACKAGES permission | High risk | Play only. REQUEST_INSTALL_PACKAGES is requested: high risk (allowed only when installing other packages is core functionality, e.g. file managers, browsers, enterprise management). Reject when the DEX also shows a self-update chain: an install path (PackageInstaller calls or the install intent) and an .apk download URL string — a Play app may not update itself outside Google Play. When the DEX could not be read, the permission alone is reported. | Google Play |
SIDE-05 | Self-update chain | High risk | Outside Play. High risk when all three are present: REQUEST_INSTALL_PACKAGES, an install path in the DEX (PackageInstaller calls or the install intent MIME type / action), and an .apk download URL string. Advice with the permission and an install path but no URL string (URLs are often built at run time). Unknown when the DEX scan was partial and nothing was found. | Outside Play (sideload) |
SIGN-06 | v1 (JAR) signature does not match the contents | Will be rejected | When a v1 / JAR signature is present: every MANIFEST.MF digest is compared with the entry, entries outside META-INF/ that the manifest does not list are reported, and the .SF digests of MANIFEST.MF are checked (the PKCS#7 signature over the .SF is not verified). APK: reject when v1 is the only scheme or minSdk < 24 (Android 7.0+ verifies v2/v3 instead and ignores v1); advice otherwise. AAB (signed JAR-style with the upload key): high risk — Google does not document how a broken upload signature is handled. | Google Play / Outside Play (sideload) |
SIGN-07 | v2 / v3 signature does not verify | Will be rejected | APK only. The 1 MiB-chunked content digest of each v2 / v3 / v3.1 signer is recomputed and its signature verified with WebCrypto. Reject when a digest differs or a signature fails (Android will not install it). Unknown for algorithms WebCrypto lacks (DSA) or when verification hit a limit. v3.2 (Android 17 hybrid ML-DSA) blocks are not verified. | Google Play / Outside Play (sideload) |
SIGN-09 | Signer differs from the previous release | Will be rejected | Only when you add the previous release with "Compare with previous release". The signer certificates (SHA-256) of both files are compared; a v3 / v3.1 proof-of-rotation lineage that contains every previous signer counts as a match (its own signatures are not verified). APK → APK: reject (the update will not install over the old version). When an AAB is involved: high risk — the AAB carries your upload key, and Play App Signing re-signs the APKs users get, so a new upload key needs an upload key reset in Play Console. | Google Play / Outside Play (sideload) |
PRIV-01 | Third-party SDKs to check in the Google Play SDK Index | Advice | Lists every SDK identified (manifest components and meta-data, native libraries, version files, DEX class prefixes and version constants). Advice: the SDK Index has no public API, so outdated or policy-flagged versions are not checked offline — look each one up. | Google Play / Outside Play (sideload) |
PRIV-02 | Data safety form: draft checklist | Advice | Play only. A checklist of data types the Data safety form probably has to cover, from permissions and identified SDK categories. The tool cannot see your Play Console answers, so this is a draft to compare against, never a verdict. | Google Play |
PRIV-03 | Advertising ID permission and declaration | High risk | Play only. High risk: an ads or analytics SDK is present, targetSdk ≥ 33 and AD_ID is not declared (getId() then returns zeros; if you removed it on purpose, say so in the declaration). Advice: AD_ID is declared — the Play Console advertising ID declaration and the Data safety "Device or other IDs" answer must match. | Google Play |
PRIV-04 | Non-resettable device identifiers | High risk | High risk: code outside AndroidX / Google libraries calls TelephonyManager getDeviceId / getImei / getMeid / getSubscriberId / getSimSerialNumber or Build.getSerial. Advice: those calls only inside AndroidX / Google library code (compatibility wrappers), a Build.SERIAL field reference, Settings.Secure.getString with the "android_id" string (SSAID), or only READ_PHONE_STATE / READ_PRIVILEGED_PHONE_STATE. Unknown when the DEX scan was partial. | Google Play / Outside Play (sideload) |
PRIV-05 | Installed-apps inventory collected | High risk | High risk: DEX code calls getInstalledPackages / getInstalledApplications, QUERY_ALL_PACKAGES is requested and an ads or analytics SDK is present. Advice: the calls with only one of the other two. Unknown when the DEX scan was partial and nothing was found. | Google Play / Outside Play (sideload) |
PRIV-06 | Sign-in found: account deletion requirement | Advice | Play only. A sign-in SDK is identified (Firebase Authentication, Google Sign-In / Credential Manager, Facebook Login, WeChat, QQ, Weibo, LINE, …). Only a reminder: the tool cannot tell whether the app lets users create accounts. Resource strings ("sign up") are not scanned. | Google Play |
PROT-02 | R8 optimization / obfuscation not enabled | Advice | Pass when a ~~R8 marker (written by R8 into the DEX string pool) is found. Advice when only ~~D8 markers are found (no shrinking; "compilation-mode":"debug" is called out), or, without any marker, when fewer than 5% of 50+ classes have R8-style 1–2 letter names. BuildConfig classes that still carry DEBUG are listed. Unknown when there is no marker and the heuristic is inconclusive. | Google Play / Outside Play (sideload) |
PROT-03 | Play Integrity API | Advice | Play scenario: advice when no class under com.google.android.play.core.integrity is found. Sideload only: advice when it is found (copies installed outside Play are reported as not licensed). Advice whenever the SafetyNet Attestation API is still called (turned down in January 2025). | Google Play / Outside Play (sideload) |
PROT-04 | Run-time loading of dex or native code | Will be rejected | Play: reject when code is loaded at run time (DexClassLoader, InMemoryDexClassLoader, PathClassLoader, System.load / Runtime.load) and a remote .dex/.jar/.so URL string is present; high risk for DexClassLoader / InMemoryDexClassLoader calls outside Google / AndroidX libraries; advice otherwise (PathClassLoader or System.load only, known libraries only, or code files shipped in assets/). Outside Play the same findings are at most high risk. | Google Play / Outside Play (sideload) |
You choose a scenario before the check runs: Google Play, outside Play (sideloading or other stores), or both. Play-only rules such as the permission declarations are skipped when you only distribute outside Play, and install-time rules such as SIDE-01 apply everywhere.
You can also pick the date the rules are evaluated against. It defaults to today. Choosing a later date shows which requirements will start to bite, for example the 16 KB rule after 2027-02-01.
Code (DEX), SDKs and signatures
The analyzer also reads the compiled code. Every classes*.dex file is parsed: in an APK at the root, in an AAB under base/dex/ and each feature module’s dex/ directory. It reads DEX versions 035 to 041, including the 041 container format, and reports any other version it finds. For each file it checks the header checksum (Adler-32) and SHA-1 signature, and a mismatch is shown as a warning.
It does not decompile anything. From the code it only collects:
- Framework API references and their call sites, found by scanning
invoke-*instructions. Examples arePackageInstaller,DexClassLoader,TelephonyManager.getImeiandgetInstalledPackages. A finding names the class that calls the API, not just the reference. - A few string patterns:
.apkand.dex/.jar/.sodownload URLs, the install-intent MIME type, andandroid_id. - The D8/R8 compiler markers that tell a shrunk release build from a plain one.
- Class names, used for SDK fingerprints (for example
com.google.firebase.*,io.sentry.*,com.applovin.*), and the version constants of a few open-source SDKs that store one.
Those facts drive the code-level checks:
- self-update (PLAY-12, SIDE-05)
- the SDK list and the Data safety draft (PRIV-01, PRIV-02)
- the advertising ID (PRIV-03)
- device identifiers (PRIV-04)
- the installed-apps inventory (PRIV-05)
- sign-in and account deletion (PRIV-06)
- R8 (PROT-02)
- Play Integrity (PROT-03)
- run-time code loading (PROT-04)
The Classes and methods browser under “Code (DEX)” lists every class by package with its fields, methods and access flags, and you can filter it by name. Lists are capped and expand on demand, so very large apps stay responsive.
Signatures are now verified, not just read. For v2, v3 and v3.1, the 1 MiB-chunked content digest is recomputed and the signature checked with your browser’s WebCrypto (SIGN-07). For v1 or JAR signatures, including the upload signature on an AAB, every MANIFEST.MF digest is compared with the file it covers (SIGN-06). With Compare signer with the previous release, you add the previous APK or AAB, and the tool checks that both are signed with the same key or a v3 rotation lineage (SIGN-09).
Flutter apps
When the package contains Flutter (libflutter.so or flutter_assets/), the report adds Flutter build details. It reads them from the file, from a few bounded reads:
- Per ABI: whether
lib/<abi>/libflutter.so(the engine) andlibapp.so(your Dart code, compiled ahead of time) are present. In an AAB these are underbase/lib/<abi>/. - Dart snapshot hash and VM features, from the snapshot header near the start of each
libapp.so. The hash is a 32-character value tied to the Dart SDK that compiled the app; the tool shows it but does not map it to a version. - Dart version as compiled into
libflutter.so, for example3.11.1 (stable) (Tue Feb 24 00:03:07 2026 -0800) on "android_arm64". - Build mode: debug (JIT) when
flutter_assetscontainskernel_blob.bin,isolate_snapshot_dataorvm_snapshot_data. Release or profile (AOT) comes from the first VM feature inlibapp.so(productorrelease). That second rule was checked on Flutter 3.41.4 and Flutter Gallery 2.9.2 builds; treat it as a heuristic for other versions. - Where
flutter_assetsis (assets/orbase/assets/), how many files it has, and the asset and font counts fromAssetManifestandFontManifest.json.
It reads only the first 256 KiB of each libapp.so and the first 4 MiB of one libflutter.so. When a value is not in that window, it is left out rather than guessed. To go further into the Dart code, see the Flutter guide under Related guides.
How your file stays private
The file is never uploaded. That is a property of how the page is built, not only a promise:
- The page hands the browser’s
Filehandle to a Web Worker. The worker reads slices of the file and decompresses entries with the browser’s built-inDecompressionStream. It contains no network code at all. - Only the parts that matter are read: the ZIP central directory,
AndroidManifest.xml, the resource table (resources.arsc, orresources.pbin an AAB), the launcher icon, the accessibility-service and network-security-config XML when the manifest points to them, the first bytes of each.soand of the React Native bundle, small version files, and the signing data. A 200 MB APK is analyzed by reading a few megabytes. - After the report is shown, the whole file is hashed with SHA-256 in 4 MB slices, still inside the worker. The hash is shown on the page and included in your own exports. Next to it is a link that searches the hash on VirusTotal: a plain link, so nothing is requested until you click it, and even then only the hash is in the address. The file itself is never uploaded anywhere.
- The analytics event sent after an analysis carries only coarse, fixed values: file kind, a size bucket, a duration bucket, success or an error code, and the number of failed checks. It never contains the file name, the package name, a hash or any file content. The full list is on the privacy page.
- The tool page shows no ads.
How to verify it yourself
You do not have to take our word for it.
- Open your browser’s developer tools and switch to the Network tab before you choose a file. Run an analysis and look at every request that appears. None of them carries your file. Apart from this site, the only hosts you should see are Google Fonts and Umami (
cloud.umami.is,gateway.umami.is). - Or load this page, then disconnect from the network (turn off Wi-Fi, or tick Offline in the Network tab), and run the analysis. It completes, because nothing needs to leave your machine.
How to read the results
Each check ends in one of five states:
- Will be rejected: if the rule applies to your scenario, Google Play will block the release, or Android will refuse to install the package.
- High risk: likely to trigger a policy declaration review, a Play Protect warning, or a requirement that becomes mandatory on a known date.
- Advice: a best practice or an early warning.
- Unknown: the tool could not decide. Typically a value is a resource reference it cannot resolve yet, or the part of the file the rule depends on failed to parse. Unknown is not a pass. Check those items by hand.
- Passed: the tool looked and found nothing.
Every failed check lists its evidence, for example lib/arm64-v8a/libfoo.so: PT_LOAD p_align=0x1000, so you can confirm the finding with your own tools before changing anything.
A clean report is not an approval. Google Play reviews policy declarations, store listings, Data safety answers and runtime behavior that no file inspection can see. The results do not represent Google’s review decision. For source-level checks inside Android Studio, Google also offers Play Policy Insights.
Fixing the most common problems
16 KB page size (PLAY-02, PLAY-03)
Apps that target Android 15 or higher must support 16 KB memory pages on 64-bit devices. Google’s developer documentation states that from 2027-02-01, updates that do not comply cannot be published. Until then the tool reports these checks as high risk, not as rejections. Source: Support 16 KB page sizes.
- ELF alignment (PLAY-02). Build native code with NDK r28 or later, which aligns to 16 KB by default. With NDK r27 or earlier, add the linker flag
-Wl,-z,max-page-size=16384. For prebuilt.sofiles from an SDK vendor, ask the vendor for a 16 KB compatible version. - ZIP alignment (PLAY-03). Use Android Gradle Plugin 8.5.1 or later, which aligns uncompressed libraries correctly. If you cannot upgrade, set
useLegacyPackaging = trueso the libraries are compressed instead (installs get larger). You can confirm the result withzipalign -c -P 16 -v 4 app.apk.
targetSdk 36 (PLAY-01)
Since 2026-08-31, new apps and app updates for phones and tablets must target API level 36 (Android 16). You can request an extension in Play Console that runs until 2026-11-01. Wear OS and Android Automotive OS apps must target 35, and TV and XR apps 34; the tool reads uses-feature in the manifest to pick the right threshold. Source: Target API level requirements.
Raising targetSdk changes runtime behavior, so walk through the Android 16 behavior changes and test before you ship. The opposite case, SIDE-01, matters even outside Play: Android 14 refuses to install apps that target below API 23, and Android 15 and later refuse below API 24 (Android 15 behavior changes).
Debug certificate (SIGN-01)
A certificate whose subject is CN=Android Debug,O=Android,C=US is the key Android Studio generates for local builds. Google Play rejects it, and its private key sits on every developer machine, so anyone could sign an “update” that replaces yours. Create a dedicated release key (RSA 2048 bits or more), back it up, and sign release builds with it. On Play, enroll in Play App Signing so Google holds the app signing key and you keep an upload key. Sources: Sign your app, Play App Signing.
Only a v1 signature (SIGN-03)
The v1 (JAR) scheme signs individual ZIP entries and leaves the rest of the archive unprotected. Apps that target API 30 or higher must be signed with APK Signature Scheme v2 or later, or they will not install. The Android Gradle Plugin enables v2 and v3 by default, so a v1-only APK usually comes from a custom signing step. Sign with apksigner and keep v2/v3 enabled; once minSdk is 24 or higher you can drop v1. Always run zipalign before apksigner, never after. Sources: APK signing, apksigner, zipalign.
Permission findings (PLAY-08, 09, 10, 12, 13, 16)
These are not automatic rejections of the permission itself. They mean Google Play only allows the permission for specific core uses and asks you to fill in a declaration. If your app does not need it, remove it, including from libraries that merge it into your manifest. Otherwise use the narrower alternative: the SMS Retriever API instead of SMS permissions, <queries> instead of QUERY_ALL_PACKAGES, the Storage Access Framework or MediaStore instead of all files access, the system Photo Picker instead of READ_MEDIA_IMAGES. Note that from 2027-01-27 phone account verification is no longer an allowed use of READ_CALL_LOG (Policy deadlines). Each finding in the report links to its policy page: SMS and Call Log, package visibility, all files access, REQUEST_INSTALL_PACKAGES, background location, photo and video permissions.
AAB files and how to cross-check them with bundletool
An Android App Bundle stores its manifest and resource table as protobuf rather than binary XML. The tool decodes the base module’s manifest/AndroidManifest.xml, resources.pb and BundleConfig.pb, so an AAB gets the same manifest checks as an APK. References such as @string/app_name and @mipmap/ic_launcher are resolved to their default value and shown next to the raw resource ID. The report also lists the bundle’s modules (base, feature modules and asset packs, with their delivery mode) and the BundleConfig.pb settings that matter for 16 KB support: whether native libraries are kept uncompressed and at which page alignment.
The launcher icon is shown when it is a PNG or WebP bitmap; the highest-density one is picked. An adaptive icon is vector XML, which the tool does not render; it says so and shows the foreground bitmap when there is one.
To check the tool against Google’s own reader, run bundletool locally:
java -jar bundletool.jar dump manifest --bundle=app.aab
java -jar bundletool.jar dump resources --bundle=app.aab --values
java -jar bundletool.jar dump config --bundle=app.aab
The package, versionCode, versionName, min/target SDK, permissions, debuggable, label and icon should match the report. bundletool prints references by name (@string/app_name); the resource dump maps that name to the ID shown in the report and its values.
What it does not do
- No malware verdict. It does not scan for viruses and does not say whether an app is safe to install.
- No DEX decompilation. It does not turn
classes.dexinto Java or Kotlin, and it reads no bytecode beyondinvoke-*targets. For that, use a local decompiler such as JADX (see the guides below). - No AAB conversion. It cannot turn an AAB into an installable APK in the browser; use
bundletoolfor that. It reads the bundle as it is: there are no generated split APKs to check, so the ZIP alignment check (PLAY-03) and the v1-only signature check (SIGN-03) apply to APKs only. - Static evidence only. SDKs are found by file names, manifest entries, version files and DEX class prefixes. R8 can rename or repackage SDK classes, so an SDK that is not listed may still be there. A version is shown only when the package states it. The code checks see which APIs are referenced and called and which strings exist. They do not follow data flow or code that is built or downloaded at run time. When the DEX scan is incomplete, a rule that depends on something being absent says Unknown. The Data safety list is a draft to compare with your own answers. The SDK Index has no public API, so SDK policy flags are not checked.
- Packers are identified, never handled. Packing and app hardening are legitimate protection. PROT-01 names what it found and explains the false-positive and Play App Signing risks. The tool never unpacks, modifies, re-signs or repackages anything, and it gives no advice on hiding behavior from reviewers or scanners.
- Signature verification has limits. DSA signatures cannot be verified with WebCrypto, so they show as Unknown. APK Signature Scheme v3.2, Android 17’s hybrid ML-DSA scheme, is not verified. For v1, the digests are checked but the PKCS#7 signature over the
.SFfile is not. The v3 rotation lineage is read but its own signatures are not verified.apksigner verify -vremains the reference. - No review decision. Results do not represent Google’s review decision, and a clean report does not guarantee approval.
FAQ
Is the file really not uploaded? Yes. Analysis runs in a Web Worker in your tab. You can confirm it with the Network tab or by going offline, as described in How to verify it yourself.
Which file types are supported?
.apk and .aab up to 1 GiB. Split APK sets (.apks, .xapk) are not supported in the first version.
Why do I get “Unknown”?
A value the check depends on could not be read, for example a targetSdkVersion written as a resource reference, or a damaged signing block. Treat Unknown as “check this yourself”, not as a pass.
Does a clean report mean Google Play will approve my app? No. Play also reviews declarations, store listing, Data safety and runtime behavior. The tool only covers what can be read from the file.
How current are the rules? Each rule records when it was last checked against its official source and is reviewed monthly. A rule not checked for 90 days is marked as needing review in the report.
Related guides
- Check an APK for Android’s 16 KB Page Size Requirement: PLAY-02 and PLAY-03 step by step on sample APKs, cross-checked with
zipalignandllvm-readelf. - Android APK Analysis with REA: JADX, Apktool, ADB and Flutter: go from this report to the code that caused a finding.
- Android CTF for Beginners: Reverse a Practice APK, Find the Flag: use the DEX class browser to find the check in a practice APK, then read it in JADX and solve it.
- Flutter APK Reverse Engineering: libapp.so and Dart with REA: when this report says Flutter, read the Dart snapshot hash, engine build IDs and package URIs from
libapp.so. - AAB to APK and APK Unpack: Extract Assets and Scripts with REA: convert an AAB with
bundletooland inventory every entry. - App Not Installed: Package Conflicts With an Existing Package: compare the signing certificate SHA-256 of two builds and fix the mismatch.
- App Not Installed as Package Appears to Be Invalid: turn the generic installer message into the real INSTALL_PARSE_FAILED code, then fix signing (SIGN-03), resources.arsc (SIGN-08) or split APKs (SIDE-08).
- App Isn’t Compatible With Your Phone: targetSdk, ABI, minSdk: read SIDE-01, PLAY-04 and the native library table against the real INSTALL_FAILED code.
- Android Developer Verification: Package Name and SHA-256 Cert: get the values the registration asks for from your own APK or AAB.
- Play Protect Blocked Your App? A Developer’s Guide to the Fix: read SIDE-02, SIDE-03, SIDE-06 and PROT-01, fix your build, then use the official appeal form.
- Debug Android App Crashes with adb logcat and bugreport in REA: when the build installs but crashes, read the crash log and the native tombstone.
- Install REA MCP and Connect Claude Code, Cursor and Codex: set up the local tool for deeper analysis.
- Is REA Safe? Install the Real Reverse Engineer Anything from npm: make sure the package you install is the genuine one.
- JADX vs JEB vs Apktool: Which Android Decompiler for Which Job: when you need the code behind a finding, which decompiler to open next.
- Identify an Unknown File Type by Its Magic Bytes: tell an APK from other ZIP-based files such as NuGet packages, JARs and Office documents before you open it here.