rea-reverse

Online APK & AAB analyzer and Google Play pre-launch check

Drop an APK or AAB to read its manifest, native libraries and signing certificates, and to check it against the Google Play and sideloading rules that most often block a release.

Your file never leaves your browser. Parsing runs locally in a Web Worker. Nothing is uploaded. How to verify this yourself

Drop an .apk or .aab file here

or

The file is read in your browser. Up to 1 GiB.

Where will you ship it?

Defaults to today. Pick a future date to see upcoming requirements.

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.

IDWhat is checkedSeverityWhen it firesApplies to
PLAY-01targetSdkVersion below the Google Play requirementWill be rejectedPlay 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-0216 KB page size: ELF segments not 16 KB alignedWill 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-0316 KB page size: uncompressed .so not 16 KB aligned in the ZIPWill 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-04Missing 64-bit native librariesWill be rejectedPlay 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-06Debuggable or test-only buildWill be rejectedPlay only. application android:debuggable="true" or android:testOnly="true".Google Play
PLAY-07Package name uses a reserved prefixWill be rejectedPlay only. Package name com.example.*, com.android.* or android.*.Google Play
PLAY-08SMS or Call Log permissionsWill be rejectedPlay only. Any SMS or Call Log permission is requested.Google Play
PLAY-09QUERY_ALL_PACKAGES permissionHigh riskPlay only. QUERY_ALL_PACKAGES is requested (high risk: declaration review).Google Play
PLAY-10All files accessWill be rejectedPlay only. MANAGE_EXTERNAL_STORAGE → reject; requestLegacyExternalStorage="true" alone → high risk.Google Play
PLAY-13Background location permissionHigh riskPlay only. ACCESS_BACKGROUND_LOCATION is requested.Google Play
PLAY-16Photo and video permissionsWill be rejectedPlay only, and only when targetSdk ≥ 33: READ_MEDIA_IMAGES or READ_MEDIA_VIDEO is requested.Google Play
SIDE-01targetSdkVersion too low to install on new Android versionsWill be rejectedOutside Play. targetSdk < 24 (Android 15+ refuses to install) or < 23 (Android 14+).Outside Play (sideload)
SIGN-01Signed with an Android debug certificateWill be rejectedAny 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-03Only a v1 (JAR) signatureWill be rejectedAPK 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-05An APK instead of an Android App Bundle for Google PlayHigh riskPlay 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-11Accessibility service declaredHigh riskPlay 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-14Exact alarm permissionHigh riskPlay 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-15Foreground service types and full-screen intentsHigh riskOnly 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-17Contacts permission (new Play rule)AdvicePlay 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-02Old targetSdk: Play Protect warning on installHigh riskOutside 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-03Permissions blocked for sideloaded apps by enhanced fraud protectionHigh riskOutside 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-04Accessibility service marked as an accessibility toolHigh riskOutside 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-06High-risk combination of capabilitiesHigh riskOutside Play. Three or more of: SYSTEM_ALERT_WINDOW, an accessibility service, SMS permissions, a device admin receiver, a notification listener.Outside Play (sideload)
SIDE-07No launcher entryAdviceOutside 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-08Split APK shipped as if it were a complete appWill be rejectedOutside 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-09Non-standard ZIP structureHigh riskAPK 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-10Android developer verification: what to registerAdviceOutside 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-02Signed with a publicly known keyWill be rejectedA 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-04Weak signing key or certificate digestHigh riskRSA 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-05Signing certificate expires before 22 October 2033Will be rejectedPlay 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-08ZIP alignment and resources.arsc storageWill be rejectedAPK 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-07Cleartext traffic or user-installed CAs trustedAdviceusesCleartextTraffic="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-01App protection (packer / hardening) identifiedAdviceIdentification 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-12REQUEST_INSTALL_PACKAGES permissionHigh riskPlay 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-05Self-update chainHigh riskOutside 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-06v1 (JAR) signature does not match the contentsWill be rejectedWhen 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-07v2 / v3 signature does not verifyWill be rejectedAPK 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-09Signer differs from the previous releaseWill be rejectedOnly 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-01Third-party SDKs to check in the Google Play SDK IndexAdviceLists 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-02Data safety form: draft checklistAdvicePlay 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-03Advertising ID permission and declarationHigh riskPlay 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-04Non-resettable device identifiersHigh riskHigh 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-05Installed-apps inventory collectedHigh riskHigh 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-06Sign-in found: account deletion requirementAdvicePlay 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-02R8 optimization / obfuscation not enabledAdvicePass 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-03Play Integrity APIAdvicePlay 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-04Run-time loading of dex or native codeWill be rejectedPlay: 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 are PackageInstaller, DexClassLoader, TelephonyManager.getImei and getInstalledPackages. A finding names the class that calls the API, not just the reference.
  • A few string patterns: .apk and .dex/.jar/.so download URLs, the install-intent MIME type, and android_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) and libapp.so (your Dart code, compiled ahead of time) are present. In an AAB these are under base/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 example 3.11.1 (stable) (Tue Feb 24 00:03:07 2026 -0800) on "android_arm64".
  • Build mode: debug (JIT) when flutter_assets contains kernel_blob.bin, isolate_snapshot_data or vm_snapshot_data. Release or profile (AOT) comes from the first VM feature in libapp.so (product or release). 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_assets is (assets/ or base/assets/), how many files it has, and the asset and font counts from AssetManifest and FontManifest.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 File handle to a Web Worker. The worker reads slices of the file and decompresses entries with the browser’s built-in DecompressionStream. 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, or resources.pb in an AAB), the launcher icon, the accessibility-service and network-security-config XML when the manifest points to them, the first bytes of each .so and 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.

  1. 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).
  2. 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 .so files 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 = true so the libraries are compressed instead (installs get larger). You can confirm the result with zipalign -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.dex into Java or Kotlin, and it reads no bytecode beyond invoke-* 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 bundletool for 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 .SF file is not. The v3 rotation lineage is read but its own signatures are not verified. apksigner verify -v remains 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.