Lab 6 — Permissions and AppOps
Two systems decide what an app may do: permission grants and per-operation AppOps. Learn to read both, revoke a grant, and see why a permission can be granted while an operation is still denied.
Permissions are the part of Android everyone thinks they understand and almost nobody can inspect. Two systems are at work: the permission grant, which is a lasting decision, and AppOps, which is a per-operation switch that can be flipped later. This lab reads both.
TL;DR#
pm list permissionsanddumpsys packageshow declared and granted permissions.dumpsys package <pkg>has separate sections for requested and install permissions; the difference is the grant.- AppOps is the second system: per-operation, per-app switches that can deny an action even when the permission is granted.
cmd appops getreads a package's operation modes;cmd appops setchanges one (and is reversible). The modes areallow,ignore,denyanddefault.- Permission auto-reset and one-time grants mean a grant can disappear without the app doing anything.
Mental model#
Think of a building with two kinds of access control.
The permission is a key card: once issued, it opens the door until it is revoked. It is a durable authorisation.
The AppOp is the door's own sensor: it can refuse to open even for a valid card, because the building changed its policy, or because the occupant asked for privacy, or because the door is in a restricted mode.
Both must allow the action. That is why an app can hold a permission and still be denied.
Where the analogy breaks#
A door sensor is physical and its state is obvious. AppOps is invisible to apps: the API returns a failure, and the app cannot tell whether the permission is missing or the operation was denied. That indistinguishability is deliberate — it prevents probing — and it is why developers often misdiagnose an AppOps denial as a permission problem.
Deep dive#
The permission model#
A permission has a protection level that determines how it can be granted: normal (granted at install), dangerous (runtime prompt), signature (only same-signer apps), and several privileged levels for system apps. The manifest declares what the app wants; the platform decides what it gets.
Read it:
adb shell dumpsys package com.example.app | grep -A 20 "requested permissions"
adb shell dumpsys package com.example.app | grep -A 20 "install permissions"The gap between requested and installed is the interesting part. A requested permission that is not in the install list is either not granted yet (dangerous, awaiting a prompt) or not grantable to this app (signature-level, wrong signer).
Runtime permissions#
Dangerous permissions are granted at runtime, per user. The platform records the grant, and the user can revoke it at any time in Settings. Three mechanisms make grants less permanent than they look:
- One-time grants. The app gets access until it leaves the foreground, then it is revoked.
- Auto-reset. An app unused for a long period has its runtime grants reset.
- Selected photos. For media access, the user can grant access to specific items rather than the whole library.
An app must handle a revoked grant at any time, which is why the permission check and the operation can disagree: the check happened, then the grant went away.
AppOps#
AppOps is a system of named operations with a mode: allowed, ignored, errored or default. It is consulted when an app performs an operation, and it is checked in addition to the permission.
Read it:
adb shell cmd appops get com.example.appThe output lists operation names and modes. Operation names are internal (OP_CAMERA,
OP_FINE_LOCATION, and so on) and are not the same as permission names, which is the first thing
to understand: one permission can gate several operations.
Change one:
adb shell cmd appops set com.example.app COARSE_LOCATION ignore
adb shell cmd appops set com.example.app COARSE_LOCATION defaultignore makes the operation fail as if denied; default restores the normal behaviour. It is
reversible and it is the same mechanism the privacy UI uses.
Where the two disagree#
The classic confusing case: an app holds CAMERA and still gets a failure. Possible causes:
- The camera AppOp is set to
ignore(privacy toggle, or a policy). - The app is in the background and background camera access is restricted.
- A one-time grant expired.
- The device has a hardware or policy restriction.
Reading both systems is how you tell them apart. dumpsys package gives the grant;
cmd appops get gives the operation mode.
The usage record#
Android records permission usage for the privacy dashboard. The data is available through the
permission usage APIs and through dumpsys. It answers "which app used the camera, and when?" and
it is the same data the user sees.
This is worth knowing because it changes how you debug: an app that is denied an operation may still appear in the usage record as having attempted it, depending on the operation.
Walkthrough#
- Read requested permissions
adb shell dumpsys package com.android.settings | grep -A 20 "requested permissions"You should seeA list of permission names. - Read granted permissions
adb shell dumpsys package com.android.settings | grep -A 20 "install permissions"You should seeA shorter list under install permissions. - Read AppOps
adb shell cmd appops get com.android.settingsYou should seeOperation names with modes. - Read all declared permissions
adb shell pm list permissions -g -d | head -40You should seeA long list grouped by protection level. - Find a specific permission
adb shell dumpsys package com.android.settings | grep -i camera}You should seeThe permission record with its protection level.
adb shell dumpsys package com.android.settings | grep -A 20 "requested permissions"
adb shell dumpsys package com.android.settings | grep -A 20 "install permissions"
adb shell cmd appops get com.android.settings
adb shell pm list permissions -g -d | head -40
adb shell dumpsys package com.android.settings | grep -i cameraExample output
Illustrative output — permission sets differ per device.Challenges#
Challenge 1 — Find a permission that is requested but not granted#
Pick a package and identify a permission in the requested list that is absent from the installed list.
Reveal the answer
adb shell dumpsys package <package> | grep -A 40 "requested permissions"
adb shell dumpsys package <package> | grep -A 40 "install permissions"A runtime permission that has not been prompted for will appear in requested and not in installed. A signature permission that the app's signer does not hold will also be missing, and it will never be grantable. The protection level tells you which case you are in.
Challenge 2 — Deny an operation without revoking a permission#
Use AppOps to make a granted operation fail, then restore it.
Reveal the answer
adb shell cmd appops get <package>
adb shell cmd appops set <package> COARSE_LOCATION ignore
adb shell cmd appops get <package> | grep -i location
adb shell cmd appops set <package> COARSE_LOCATION defaultThe permission grant is unchanged; only the operation mode changes. This is the mechanism behind privacy toggles, and it is why "the permission is granted" does not imply "the operation works".
Challenge 3 — Explain a silent failure#
An app reports that a location call returns null. It holds ACCESS_FINE_LOCATION. Give two
plausible causes and the command that distinguishes them.
Reveal the answer
adb shell dumpsys package <package> | grep -i location
adb shell cmd appops get <package> | grep -i location
adb shell dumpsys location | head -40Two causes: the grant was revoked or was one-time and expired (the package dump shows the current grant state), or the AppOp is ignored (the appops output shows the mode). The location dump shows the provider state and recent requests, which distinguishes a platform-level problem from an app-level one.
What you learned#
You can read both authorization systems and explain a denial that the permission model alone cannot explain. That is the difference between guessing at a privacy bug and locating it.
Next#
Lab 7 — Settings and properties covers the configuration databases that both systems read from.
Sources#
Sources
Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.
- docsPermissions best practicesdeveloper.android.comaccessed 2026-10-27↩
- docsdumpsysdeveloper.android.comaccessed 2026-10-27↩
- docsPermissions on Android | Privacydeveloper.android.comaccessed 2026-10-27↩
- docsDeclare app permissions | Privacydeveloper.android.comaccessed 2026-10-27↩
- docsRequest runtime permissions | Privacydeveloper.android.comaccessed 2026-10-27↩
- docsBehavior changes: Apps targeting Android 13 or higherdeveloper.android.comaccessed 2026-10-27↩