Skip to main content

Google Play reads your manifest, not your code path

Google Play's restricted-permission rules are written against what an app declares, not against what it calls. An app that never invokes an API but carries the permission line in its merged manifest — because a library put it there — has a store problem before it has a runtime one. The SMS and Call Log groups, QUERY_ALL_PACKAGES, and the eight permissions barred outright from personal loan apps, read off Google's own policy pages. Read 3 September 2026.

By Shalabh D & Abhinav A
September 3, 20265 min read

Most permission advice is about runtime: request it when you need it, explain why, handle the refusal. That advice is correct and it is not what this page is about.

Google Play's restricted-permission rules are written against what your app declares, and the declaration Google reads is the merged manifest of the artefact you uploaded — not the manifest in your source tree, and not the set of APIs your code actually calls.

Read 3 September 2026, from Google's policy pages rather than from a summary of them.

The sentence that makes it a build question

Under the SMS and Call Log restrictions, after the table of permission groups:

"Apps lacking default SMS, Phone, or Assistant handler capability may not declare use of the above permissions in the manifest. This includes placeholder text in the manifest."

Read that twice, because the second sentence is doing something unusual. It is not enough to never call the API. It is not enough to leave the permission unrequested at runtime. The line may not be present, and Google says explicitly that a line left in as a placeholder is still a line.

A runtime rule can be satisfied by a code path that is never taken. A manifest rule cannot be satisfied by anything except the absence of the entry.

What the two groups actually contain

Google states them as a table of restricted permissions against the requirement each one carries, and its lists are introduced with "for example" — they are illustrative rather than closed:

Restricted permissionRequirement
Call Log permission group — READ_CALL_LOG, WRITE_CALL_LOG, PROCESS_OUTGOING_CALLS"It must be actively registered as the default Phone or Assistant handler on the device."
SMS permission group — READ_SMS, SEND_SMS, WRITE_SMS, RECEIVE_SMS, RECEIVE_WAP_PUSH, RECEIVE_MMS"It must be actively registered as the default SMS or Assistant handler on the device."

And the condition is continuous rather than one-off: "apps must be actively registered as the default SMS, Phone, or Assistant handler before prompting users to accept any of the above permissions and must immediately stop using the permission when they’re no longer the default handler."

QUERY_ALL_PACKAGES, and the two named tiers

Package visibility is its own policy section on the same page, and Google splits it into two levels it names.

Broad visibility, in Google's words: “Broad visibility is the capability of an app to have extensive (or “broad”) visibility of the installed apps (“packages”) on a device.” The restriction attaches to a target level, exactly as the platform behaviour does: "For apps targeting API level 30 or later, broad visibility to installed apps via the QUERY_ALL_PACKAGES permission is restricted to specific use cases where awareness of and/or interoperability with any and all apps on the device are required for the app to function."

Then the test a reviewer applies, which is a comparative one rather than a threshold: "You may not use QUERY_ALL_PACKAGES if your app can operate with a more targeted scoped package visibility declaration (for example, querying and interacting with specific packages instead of requesting broad visibility)."

Two more clauses are worth carrying because they close the obvious workarounds:

  • Approximating it by other means is covered too: "Use of alternative methods to approximate the broad visibility level associated with QUERY_ALL_PACKAGES permission are also restricted to user-facing core app functionality and interoperability with any apps discovered via this method."
  • And on what the resulting data may be used for: "Under no circumstances can data from the installed app inventory be sold or shared for advertising or analytics monetization."

This is the Play-policy half of a two-part story. The platform half — that an app targeting API level 30 or higher gets a filtered view by default whether or not it asked for one — is in the expiry list, and it is why a package-name check written before that boundary returns a clean answer rather than an error.

The eight that are barred outright

For one class of app there is no declaration to make, because the permissions are prohibited. Google's scope sentence is precise and it is worth quoting rather than paraphrasing, because "fintech" is much broader than what it binds:

"Personal loan apps, apps with the primary purpose of facilitating access to personal loans (for example, lead generators or facilitators) or lines of credit, accessory loan or credit apps (loan calculators, loan guides, etc.), and Earned Wage Access (EWA) apps are prohibited from accessing sensitive data, such as photos and contacts."

Then: "The following permissions are prohibited"READ_EXTERNAL_STORAGE, READ_MEDIA_IMAGES, READ_CONTACTS, ACCESS_FINE_LOCATION, READ_PHONE_NUMBERS, READ_MEDIA_VIDEO, QUERY_ALL_PACKAGES, WRITE_EXTERNAL_STORAGE.

Eight constants, and note which ones they are. READ_MEDIA_IMAGES and READ_MEDIA_VIDEO are the modern split of the old external-storage read, so both the legacy and current spellings are listed — an app that migrated to the new constants did not leave the list. QUERY_ALL_PACKAGES appears here as well as in its own policy, which means a lending app has two separate reasons to care about the same line.

Why this is a question about your build graph

Every rule above is applied to a document that is assembled rather than written. The merged manifest contains what your code declares plus what every library declares, and a permission contributed by a transitive dependency is indistinguishable, to Google, from one you typed.

So the answerable version of "do we have a permissions problem" is:

  1. Read the merged manifest of the artefact you actually ship — not AndroidManifest.xml in the app module.
  2. Attribute every <uses-permission> line to whatever contributed it. The build tooling can tell you; nobody usually asks it.
  3. For each line that appears in a policy above, establish which of three things is true: your app is the default handler and the requirement is met; the app has a documented core-functionality case; or the line came in with a dependency and nothing in your product needs it.

That third case is the common one and it is the reason this reads as a build problem rather than a policy problem. It is also two of the questions on the build handover sheet — the third-party SDK inventory, and which of them contribute manifest entries — because they are the questions a tester needs answered for exactly the same reason a reviewer does.

What this page does not do

It does not tell you whether your case will be accepted. Declaration forms are submitted in Play Console and Google adjudicates them; no third party reviews one, and nobody outside Google can tell you what the outcome will be.

Nor does reading a manifest discharge a policy obligation. What it does is establish what your artefact declares, which is the thing every rule above is written against — and which, on most Android codebases of any age, nobody currently has written down.

About the authors

Shalabh D

Lead — Managed Security Services

Security researcher and penetration tester passionate about making the internet safer. Active CTF player, bug bounty hunter, and hands-on practitioner across web, network, and application security.

Abhinav A

Lead — VAPT & Security Assessments

Leads Security Brigade's VAPT delivery team, having progressed from Security Consultant to Team Lead. Has executed advanced penetration tests across BFSI, fintech, QSR, and telecom — including ICICI Bank, Domino's, and Jubilant FoodWorks.

Continue reading

All articles →

What a Play Integrity verdict actually returns, and who it is evidence for

The Play Integrity API returns three families of verdict, and the labels most conversations about it are really about are not among the ones you get by default. This is what Google documents it returning, the limits Google publishes about its own product, and the reason every documented Android integrity primitive ends up producing evidence for a server rather than an answer for a device. Read 3 September 2026.

Your Data safety form is a declaration about a binary, and the binary keeps changing

Google Play's Data safety form binds every developer with a published app, it covers data handled by third-party libraries and SDKs as well as your own code, and Google's position on who answers for it is one sentence long: you alone are responsible. Which makes it the rare store obligation that is checkable — a declaration and a merged manifest are two artefacts, and they can be read against each other. Read 3 September 2026.

Android security advice that expired: fourteen statements, and the version that ended each one

Fourteen statements about Android security that were correct when somebody learned them and are not correct now. Each one is paired with the API level, the Android Gradle Plugin version or the Google turndown that falsified it, the platform page it was read from, and that page's own last-updated stamp — because Google revises these pages independently and a claim without a stamp cannot be re-checked. Read 3 September 2026.