Android Permissions Security
Android (Google)OfficialCreated by Google LLCDistributed by Android (Google)
- Skill
- Guidelines
- Security
- Apache-2.0
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a systematic approach to identifying and fixing security gaps in Android applications. It covers the secure configuration of AndroidManifest.xml, the implementation of robust IPC (Inter-Process Communication) boundaries, and the correct handling of runtime permissions. It emphasizes moving away from insecure practices like spoofable intent-based identity checks toward cryptographically secure UID verification using Binder. The guide also details how to properly define custom permissions, manage granular URI grants, and implement safe, non-blocking runtime permission request flows.
What it does
- Audits AndroidManifest.xml for weak custom permission protection levels.
- Provides patterns for cryptographically verifying caller identity via Binder.
- Guides the implementation of granular URI permission management for ContentProviders.
- Defines secure practices for protecting exported Services, Receivers, and Providers.
- Outlines a 3-state runtime permission request flow to prevent infinite loops.
- Recommends zero-permission alternatives like the Photo Picker for media access.
When to use it
- During security audits of Android application codebases.
- When configuring custom permissions for inter-app communication.
- When securing AIDL-based bound services against Confused Deputy attacks.
- When refactoring legacy code that relies on spoofable intent extras for identity.
- When implementing or updating runtime permission request logic.
Advantages
- Prevents common IPC vulnerabilities like package name spoofing.
- Enforces the principle of least privilege through granular URI grants.
- Reduces user friction by implementing standard, non-looping permission flows.
- Eliminates Confused Deputy risks by using proper Binder permission enforcement.
- Provides clear, actionable code snippets for modern Android security APIs.
Usage examples
- “How do I securely verify that a caller is an authorized partner app?”
- The agent provides a CallerVerifier implementation that uses Binder.getCallingUid() and PackageManager.hasSigningCertificate() to cryptographically verify the caller's signature.
- “My app is vulnerable to intent spoofing. How do I fix it?”
- The agent instructs you to remove all string-based package checks and replace them with Binder-based UID authentication to ensure the caller's identity is genuine.
- “How should I handle runtime permissions without creating an infinite loop?”
- The agent provides a 3-state flow pattern: checking grant status, showing rationale if needed, and launching the request, with a fallback to Settings for permanent denials.
- “How do I restrict access to my ContentProvider to only specific partner apps?”
- The agent guides you to define a custom permission with signature|knownSigner protection level and apply it to the provider, ensuring only apps with matching certificate digests can access it.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/android/android-permissions-security/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip android-permissions-security.zip -d .agents/skills
Documentation by Google LLC
Guidelines for auditing permission vulnerabilities, enforcing least privilege, securing IPC boundaries, and handling runtime permissions across the Android ecosystem.
Glossary
Runtime Permission: Dangerous permission (e.g., Camera, Location) that must be granted by the user at runtime on Android 6.0+ (API 23+).
Custom Permission: A permission defined by an app (
<permission>) to restrict access to its exported components.Protection Level: Attribute defining how the OS grants the permission (
normal,dangerous,signature,knownSigner).knownSigner: Protection flag (API 31+) granting signature-level access to partner apps matching trusted certificate digests.
SigningInfo: Object (API 28+) providing signing history and multiple signer certificates for an application.
Binder Calling Identity: Reliable UID representing the calling process obtained using
Binder.getCallingUid().URI Permission: Temporary, granular access granted to a content URI using
grantUriPermissionsand intent flags.
Prerequisites
Add
androidx.core:core-ktxandandroidx.activity:activity-ktx(orandroidx.fragment:fragment-ktx) dependencies to the app-levelbuild.gradlefile forContextCompatandActivityResultContracts.For
knownSignerpartner permissions (android:knownCerts), ensure the projectcompileSdkandtargetSdkare at least 31 (Android 12).For broadcast identity sharing (
BroadcastOptions.setShareIdentityEnabled), ensurecompileSdkandtargetSdkare at least 34 (Android 14).
1. Security audit and permission gap detection playbook
When auditing an Android project for permission and IPC security vulnerabilities, systematically inspect each area:
| Vulnerability Area | Search In / Pattern | Risk | Remediation Action |
|---|---|---|---|
| Weak Custom Permission Protection | AndroidManifest.xml with android:protectionLevel="normal", "dangerous", or "signatureOrSystem" | Unauthorized apps request or claim permission without restriction | Upgrade to signature or signature with knownSigner and android:knownCerts. |
| Overly Broad URI Permission Grants | <grant-uri-permission android:pathPrefix="/" /> or pathPrefix="" in <provider> | Leaks all provider data to third parties | Replace with explicit scoped subpath (e.g. android:pathPrefix="/shared/"). Retain android:grantUriPermissions="true". Remove any wildcard pathPrefix="/". |
| Missing ContentProvider Split Permissions | <provider> with only generic android:permission or missing android:readPermission and android:writePermission | Read-only callers execute write operations or write-only callers access read operations | Configure granular android:readPermission="...READ_DATA" and android:writePermission="...WRITE_DATA", AND declare the custom <permission> tags with android:protectionLevel="signature" (or signature with knownSigner) at the <manifest> root. |
| Spoofable Caller Identity Checks | intent.getStringExtra("calling_package"), intent.getStringExtra("sender"), callingPackage == "..." in Services | Malicious callers forge intent extras or strings | Remove string checks; authenticate caller UID cryptographically using Binder.getCallingUid() and pm.hasSigningCertificate(). |
| Bound Service Permission Bypass | checkCallingOrSelfPermission() or enforceCallingOrSelfPermission() in Binder AIDL stubs | Falls back to host app UID outside IPC or when identity is cleared, causing Confused Deputy bypass | Replace with enforceCallingPermission("...WRITE_DATA", ...) or checkCallingPermission("...WRITE_DATA"). |
| Missing Method-Level Checks in Bound Services | Bound Service AIDL methods mutating state without enforcing write permissions | Callers with read-only binding access invoke mutating write operations | Add enforceCallingPermission("...WRITE_DATA", "Caller lacks WRITE_DATA permission") inside mutating AIDL methods. |
| Flawed Runtime Permission Flow | Missing shouldShowRequestPermissionRationale(), missing permanent denial handling, or re-requesting in loops | Poor user experience, infinite loops, or inability to recover permissions | Implement 3-state flow: check grant -> check rationale dialog -> launch contract. On permanent denial, redirect to Settings.ACTION_APPLICATION_DETAILS_SETTINGS. |
| Simultaneous Location Requests | arrayOf(ACCESS_FINE_LOCATION, ACCESS_BACKGROUND_LOCATION) in one request | Rejected by Android 11+ (API 30+) | Request foreground location (ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION) first. Request ACCESS_BACKGROUND_LOCATION only after foreground is granted in a separate user step. |
| Broad Storage Permissions for Media | Requesting READ_EXTERNAL_STORAGE or READ_MEDIA_IMAGES for user image selection | Over-privilege, user privacy violation | Migrate to zero-permission Photo Picker (ActivityResultContracts.PickVisualMedia). |
| Cached Permission State | Storing permission status in var cachedLocationPermissionGranted = ... | Fails when permission is revoked or expires | Query ContextCompat.checkSelfPermission() dynamically at invocation time and wrap protected calls in try-catch blocks catching SecurityException. |
| Insecure Broadcasts | sendBroadcast(intent) without package target or receiver permission; receiver without sender authentication | Data interception or command spoofing | Set explicit package (intent.setPackage(...)), pass receiver permission, and enable BroadcastOptions.setShareIdentityEnabled(true) (API 34+) to verify sentFromPackage. |
2. Custom permissions and protection levels
When declaring custom permissions or partner certificates in the manifest, you
must follow the instructions in the <permission> element guide.
signature: Restricts access to apps signed with the same developer key.signature|knownSigner(API 31+): Restricts access to partner apps whose signing certificate digests match declared hashes inandroid:knownCerts.normalordangerous: Do not use for private inter-app IPC.signatureOrSystem: Deprecated (API 23). Upgrade deprecatedsignatureOrSystempermissions tosignature. Avoid in non-system apps.
Manifest snippets
Signature-level custom permission
Use signature permissions when restricting component access exclusively to
apps signed with the same developer certificate by declaring the <permission>
in the manifest and applying android:permission to the target component.
<permission
android:name="com.example.snippets.permission.ACCESS_SECURE_API"
android:protectionLevel="signature" /><br />
KnownSigner partner permission (API 31+)
Use signature|knownSigner permissions when granting access to specific
external partner apps by declaring the <permission> in the manifest with
android:knownCerts referencing a resource array of trusted SHA-256
certificate digests.
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- SHA-256 fingerprints of trusted partner certificates (lowercase, no colons) -->
<string-array name="trusted_partner_certs" tools:ignore="Typos">
<item>103938ee4537e59e8ee792f654504fb8346fc6b346d0bbc4415fc339fcfc8ec1</item>
</string-array>
</resources><br />
<permission
android:name="com.example.snippets.permission.PARTNER_API"
android:protectionLevel="signature|knownSigner"
android:knownCerts="@array/trusted_partner_certs" /><br />
3. Exported component protection and URI permissions
Private components : Always set
android:exported="false".Exported components : Require permissions (
android:permission,android:readPermission,android:writePermission).Granular URI Grants : Retain
android:grantUriPermissions="true"for secure sharing, but scope grants to specific sub-paths. Never use wildcardpathPrefix="/".Custom Permission Declarations : When splitting read and write permissions on a provider, you MUST declare the
<permission>tags withandroid:protectionLevel="signature"(orsignature|knownSigner) at the<manifest>root level.Remediating Existing Wildcard Grants : You MUST find and remove any existing
<grant-uri-permission android:pathPrefix="/" />orpathPrefix=""and replace it with an explicit scoped subpath (e.g.android:pathPrefix="/shared/").Granular
UriPermissionManagement : Instead of broad manifest grants, use runtimeandroid.content.UriPermissiontokens for least-privilege per-URI sharing:Grant temporary access :
context.grantUriPermission(targetPackage, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)or attachIntent.FLAG_GRANT_READ_URI_PERMISSIONto outgoing intents.Persist and release access : The receiving app calls
contentResolver.takePersistableUriPermission(uri, modeFlags)for durable access across reboots, andcontentResolver.releasePersistableUriPermission(uri, modeFlags)when durable access is no longer needed.Audit active grants : Inspect
contentResolver.persistedUriPermissions(List<UriPermission>) to verify granted URIs (uriPermission.uri,uriPermission.isReadPermission(),uriPermission.isWritePermission()).Revoke temporary access : Call
context.revokeUriPermission(targetPackage, uri, modeFlags)(API 26+), falling back tocontext.revokeUriPermission(uri, modeFlags)on API 25 and lower so access fails closed as soon as operations conclude.
Manifest snippet: Complete secure ContentProvider declaration
Declare the split read and write permissions at the <manifest> root:
<permission
android:name="com.example.snippets.permission.READ_DATA"
android:protectionLevel="signature" />
<permission
android:name="com.example.snippets.permission.WRITE_DATA"
android:protectionLevel="signature" /><br />
Then configure the provider with those permissions and a scoped URI grant:
<provider
android:name=".permissions.SecureDataProvider"
android:authorities="com.example.snippets.partnerprovider"
android:exported="true"
android:readPermission="com.example.snippets.permission.READ_DATA"
android:writePermission="com.example.snippets.permission.WRITE_DATA"
android:grantUriPermissions="true">
<grant-uri-permission android:pathPrefix="/shared/" />
</provider><br />
Runtime granular UriPermission management snippet
object UriPermissionManager {
/** Grants temporary read access to a specific URI for a partner package. */
fun grantScopedUriAccess(context: Context, targetPackage: String, uri: Uri) {
context.grantUriPermission(
targetPackage,
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
/** Audits active persisted URI permissions held by the application. */
fun getActivePersistedPermissions(context: Context): List<UriPermission> {
return context.contentResolver.persistedUriPermissions
}
/** Revokes temporary URI access for a specific partner package when data transfer completes. */
fun revokeScopedUriAccess(context: Context, targetPackage: String, uri: Uri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.revokeUriPermission(
targetPackage,
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
} else {
context.revokeUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
}
}
}<br />
4. Cryptographic caller identity verification
Never trust caller package names provided in Intents or string arguments
(getCallingPackage(), intent.getStringExtra("calling_package"),
intent.getStringExtra("sender"), intent.getStringExtra("package")). Intent
extras and string package names are forged by malicious applications.
Critical vulnerability: Package name spoofing using intent extras
// VULNERABLE PATTERN: DO NOT DO THIS
val callingPackage = intent?.getStringExtra("calling_package")
if (callingPackage != null && SignatureUtils.verifyPartnerPackage(this, callingPackage)) {
// A malicious app passes "com.example.partner" in the extra.
// The signature check verifies the installed partner on disk, but the CALLER was malicious!
}<br />
Secure pattern: Authenticate calling UID cryptographically
Completely remove all
intent.getStringExtra("calling_package"),intent.getStringExtra("sender"), andcallingPackage == "..."checks.Obtain caller identity using
Binder.getCallingUid()inside an active Binder IPC transaction (such as an AIDLStubmethod orContentProvidermethod).Verify caller certificate using
PackageManager.hasSigningCertificate()(API 28+) orPackageManager.getPackagesForUid().
Complete CallerVerifier implementation
Apply this to the service that currently performs the spoofable check.
Placement : declare
CallerVerifierin the same file as the service you are fixing (for example insideSecureDataService.kt). Do not move the verification into a new file and leave the original service untouched.Active Binder IPC context :
Binder.getCallingUid()only returns the remote caller's UID during an active Binder transaction (never insideService.onStartCommand()orService.onBind(), where it returnsProcess.myUid()). RejectcallingUid == Process.myUid()so calls outside an active IPC transaction or afterBinder.clearCallingIdentity()fail closed instead of bypassing verification.Nullability :
packageInfo.signaturesisArray<Signature>?. Copy the?: return falseguard exactly as written below. IteratingpackageInfo.signaturesdirectly fails to compile with "Non-nullable value required to call an 'iterator()' method in a for-loop."
object CallerVerifier {
private const val TRUSTED_PARTNER_SHA256 =
"A1B2C3D4E5F60708090A0B0C0D0E0F1011121314151617181920212223242526"
fun isCallerAuthorized(context: Context): Boolean {
val callingUid = Binder.getCallingUid()
if (callingUid == android.os.Process.myUid()) return false
val pm = context.packageManager
// Modern API 28+ check by UID:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val certBytes = hexStringToByteArray(TRUSTED_PARTNER_SHA256)
return pm.hasSigningCertificate(callingUid, certBytes, PackageManager.CERT_INPUT_SHA256)
}
// Fallback for legacy APIs:
val callingPackages = pm.getPackagesForUid(callingUid) ?: return false
for (pkg in callingPackages) {
if (verifyPackageSignature(pm, pkg)) {
return true
}
}
return false
}
@Suppress("DEPRECATION")
private fun verifyPackageSignature(pm: PackageManager, packageName: String): Boolean {
return try {
val packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
val signatures = packageInfo.signatures ?: return false
for (sig in signatures) {
val digest = java.security.MessageDigest.getInstance("SHA-256").digest(sig.toByteArray())
val hex = digest.joinToString("") { "%02X".format(it) }
if (hex.equals(TRUSTED_PARTNER_SHA256, ignoreCase = true)) return true
}
false
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
private fun hexStringToByteArray(s: String): ByteArray {
val len = s.length
val data = ByteArray(len / 2)
for (i in 0 until len step 2) {
data[i / 2] = ((Character.digit(s[i], 16) shl 4) + Character.digit(s[i + 1], 16)).toByte()
}
return data
}
}<br />
5. Method-level Binder enforcement in bound services (SecureBoundService.kt)
In bound services exposing AIDL Binder stubs, protect state-modifying methods
using enforceCallingPermission().
Critical security rule: Never use CallingOrSelfPermission in services
NEVER use
checkCallingOrSelfPermission()orenforceCallingOrSelfPermission().Why: During an active Binder transaction, this method checks the caller's permission. However, if the method is invoked locally (outside of an active IPC) or if the IPC calling identity is dropped, it falls back to checking the host app's permissions. Because the host app holds the permission, this fallback grants access and bypasses the security boundary, causing a Confused Deputy vulnerability.
MUST USE:
enforceCallingPermission("com.example.snippets.permission.WRITE_DATA", "Caller lacks WRITE_DATA permission")orcheckCallingPermission("com.example.snippets.permission.WRITE_DATA").
Complete SecureBoundService implementation
class SecureBoundService : Service() {
private val binder = object : IMyService.Stub() {
override fun getData(): String {
// Read-only operation guarded by manifest-level permission
return "Confidential Data"
}
override fun modifyData(newData: String) {
// MUST use enforceCallingPermission or checkCallingPermission.
// NEVER use checkCallingOrSelfPermission or enforceCallingOrSelfPermission.
[email protected](
"com.example.snippets.permission.WRITE_DATA",
"Caller lacks WRITE_DATA permission"
)
updateInternalState(newData)
Log.d("SecureBoundService", "Data modified to: $newData with proper WRITE_DATA permission check")
}
}
override fun onBind(intent: Intent?): IBinder = binder
private fun updateInternalState(data: String) {
// Internal state update logic
}
}<br />
6. Safe runtime permission workflow (RuntimePermissionsActivity.kt)
When implementing runtime permission requests or handling permission denial states, you must follow the instructions in the Request runtime permissions guide and Permissions overview. Implement a 3-state flow without caching states or creating infinite retry loops:
Check Granted State :
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED.Check Rationale : When
shouldShowRequestPermissionRationale(permission)returnstrue, show a rationale dialog and launch the permission request only from its Continue button.Launch Contract :
ActivityResultContracts.RequestPermission().Handle Permanent Denial : In the callback, if permission was denied and
!shouldShowRequestPermissionRationale(permission), guide the user toSettings.ACTION_APPLICATION_DETAILS_SETTINGS.
Complete RuntimePermissionsActivity.kt implementation
class RuntimePermissionsActivity : ComponentActivity() {
private val cameraLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
startCameraPreview()
} else {
if (!shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
// User selected 'Don't ask again' or permanently denied.
// Direct user to Application Details Settings.
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
}
startActivity(intent)
} else {
showSnackbar("Camera permission is required to preview camera feed.")
}
}
}
fun requestCameraPermissionSafely() {
val permission = Manifest.permission.CAMERA
when {
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
startCameraPreview()
}
shouldShowRequestPermissionRationale(permission) -> {
showRationaleDialog(permission)
}
else -> {
cameraLauncher.launch(permission)
}
}
}
private fun showRationaleDialog(permission: String) {
AlertDialog.Builder(this)
.setTitle("Camera permission required")
.setMessage("Camera permission is needed to preview the camera feed.")
.setPositiveButton("Continue") { _, _ ->
cameraLauncher.launch(permission)
}
.setNegativeButton("Cancel", null)
.show()
}
private fun startCameraPreview() {
Log.d("RuntimePermissionsActivity", "Camera preview started")
}
private fun showSnackbar(msg: String) {
Log.i("RuntimePermissionsActivity", msg)
}
}<br />
7. Zero-permission alternatives and sequential flows
When implementing photo or media selection, you must follow the instructions in the Photo Picker API guide to avoid requesting broad storage permissions.
Photo Picker : Prefer
ActivityResultContracts.PickVisualMedia()over requesting wide storage permissions (READ_MEDIA_IMAGES,READ_EXTERNAL_STORAGE).Sequential Location Flow : Request
ACCESS_FINE_LOCATIONandACCESS_COARSE_LOCATIONfirst. Never requestACCESS_BACKGROUND_LOCATIONsimultaneously. Only request background location in a separate step after foreground is granted.
Photo picker implementation
private val photoPickerLauncher =
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
handleImageUri(uri)
}
}
fun selectPhoto() {
photoPickerLauncher.launch(
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
)
}<br />
Sequential location request implementation
private val foregroundLocationLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val fineGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] ?: false
val coarseGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION] ?: false
if (fineGranted || coarseGranted) {
startForegroundLocationUpdates()
}
}
fun requestForegroundLocation() {
foregroundLocationLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
}
// Background location requested only AFTER foreground is granted and user explicitly opts in:
private val backgroundLocationLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
startBackgroundTracking()
}
}<br />
8. Stateless checks and error boundaries
Never cache permission states in in-memory variables (e.g.
cachedLocationPermissionGranted). CheckContextCompat.checkSelfPermission()dynamically before each operation.Wrap API calls in try-catch : Catch
SecurityExceptionaround permission-protected framework calls (e.g., location queries) to handle one-time permission revocation gracefully.
fun performLocationAccess() {
val fine = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
val coarse = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
if (fine != PackageManager.PERMISSION_GRANTED && coarse != PackageManager.PERMISSION_GRANTED) {
requestForegroundLocation()
return
}
val provider = if (fine == PackageManager.PERMISSION_GRANTED) {
LocationManager.GPS_PROVIDER
} else {
LocationManager.NETWORK_PROVIDER
}
try {
val location = locationManager.getLastKnownLocation(provider)
processLocation(location)
} catch (e: SecurityException) {
Log.e("LocationAccess", "Permission revoked at runtime", e)
}
}<br />
9. Secure broadcasts (API 34+ share identity)
Use explicit intents, required receiver permissions, or Android 14+ broadcast options:
// Sender: Enforce permission and share identity
val intent = Intent("com.example.snippets.permission.ACTION_SECRET_UPDATE").apply {
setPackage("com.example.partner") // Explicit target
}
val isUdc = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
val options = if (isUdc) {
BroadcastOptions.makeBasic().apply {
setShareIdentityEnabled(true)
}.toBundle()
} else null
sendBroadcast(intent, "com.example.snippets.permission.RECEIVE_SECRET_UPDATE", options)<br />
// Receiver: Validate sender on Android 14+
class ProtectedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == "com.example.snippets.permission.ACTION_SECRET_UPDATE") {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val sender = sentFromPackage
if (sender != "com.example.trusted_sender") {
return // Reject unauthorized sender
}
}
processUpdate(intent)
}
}
private fun processUpdate(intent: Intent) {}
}<br />
Legacy compatibility (Pre-API 34) : On API 33 and lower, never authenticate
a broadcast sender using PendingIntent.getCreatorPackage() or
PendingIntent.getCreatorUid(), because any app that acquires a trusted app's
PendingIntent can attach and send it (see Sender of Pending Intents).
Instead, restrict the <receiver> with a signature or signature|knownSigner
(API 31+) permission in the manifest; if minSdkVersion is lower than 31, use a
bound Service or ContentProvider and verify Binder.getCallingUid().
Quality checklist
DO
MUST declare
<permission android:name="..." android:protectionLevel="signature" />(orsignature|knownSigner) at the<manifest>root for custom permissions and ContentProvider split permissions.MUST explicitly set
android:exported="false"for all private components to remove the need for custom permission management.MUST authenticate callers cryptographically using
Binder.getCallingUid()and certificate hashes (PackageManager.hasSigningCertificateorgetPackagesForUid) rather than checking spoofable intent extras.MUST check
shouldShowRequestPermissionRationale()before runtime prompts and during denial handling.MUST direct user to
Settings.ACTION_APPLICATION_DETAILS_SETTINGSwhen permission is permanently denied.MUST enforce method-level checks with
enforceCallingPermission()orcheckCallingPermission()on modifying AIDL methods.MUST scope ContentProvider URI permissions to explicit sub-paths (e.g.
android:pathPrefix="/shared/") while retainingandroid:grantUriPermissions="true".MUST catch
SecurityExceptionaround protected API calls.MUST request foreground and background location permissions sequentially, never simultaneously.
NEVER
NEVER extract or verify package names from Intent extras (
intent.getStringExtra("calling_package"),intent.getStringExtra("sender"),intent.getStringExtra("package")). Always authenticate usingBinder.getCallingUid()and certificates.NEVER authenticate a broadcast or IPC sender using
PendingIntent.getCreatorPackage()orPendingIntent.getCreatorUid().NEVER remediate caller spoofing by merely adding manifest permissions while keeping spoofable intent extra checks.
NEVER use
checkCallingOrSelfPermission()orenforceCallingOrSelfPermission()to guard external caller methods. Always useenforceCallingPermission()orcheckCallingPermission().NEVER use deprecated
signatureOrSystem.NEVER declare or retain
<grant-uri-permission android:pathPrefix="/" />orpathPrefix="". Always replace with a scoped sub-path.NEVER create infinite permission request loops on denial.
NEVER request foreground and background location permissions in the same request.
NEVER cache permission grant states in in-memory fields.
Files
- ATTRIBUTION.mdAdded by AutoGineer999 B
- LICENSELicence file added by AutoGineer11.1 KiB
- SKILL.md29.2 KiB
- references/android/guide/topics/manifest/permission-element.md6.7 KiB
- references/android/guide/topics/permissions/overview.md10.4 KiB
- references/android/privacy-and-security/risks/sender-of-pending-intents.md3.7 KiB
- references/android/training/permissions/requesting.md37.9 KiB
references/android/training/permissions/requesting.md
Every Android app runs in a limited-access sandbox. If your app needs to use resources or information outside of its own sandbox, you can declare a runtime permission and set up a permission request that provides this access. These steps are part of the workflow for using permissions.
If you declare any dangerous permissions, and if your app is installed on a device that runs Android 6.0 (API level 23) or higher, you must request the dangerous permissions at runtime by following the steps in this guide.
If you don't declare any dangerous permissions, or if your app is installed on a device that runs Android 5.1 (API level 22) or lower, the permissions are automatically granted, and you don't need to complete any of the remaining steps on this page.
[!NOTE] Note: Some permissions guard access to system resources that are particularly sensitive or are not directly related to user privacy. There's a different process for requesting these special permissions.
Basic principles
The basic principles for requesting permissions at runtime are as follows:
Ask for a permission in context, when the user starts to interact with the feature that requires it.
Don't block the user. Always provide the option to cancel an educational UI flow, such as a flow that explains the rationale for requesting permissions.
If the user denies or revokes a permission that a feature needs, gracefully degrade your app so that the user can continue using your app, possibly by disabling the feature that requires the permission.
Don't assume any system behavior. For example, don't assume that permissions appear in the same permission group. A permission group merely helps the system minimize the number of system dialogs that are presented to the user when an app requests closely related permissions.
Workflow for requesting permissions
Before you declare and request runtime permissions in your app, evaluate whether your app needs to do so. You can fulfill many use cases in your app, such as taking photos, pausing media playback, and displaying relevant ads, without needing to declare any permissions.
If you conclude that your app needs to declare and request runtime permissions, complete these steps:
Declare the permissions your app might need to request in your app's manifest file.
Design your app's user experience (UX) so that specific actions in your app are associated with specific runtime permissions. Let users know which actions might require them to grant permission for your app to access private user data.
Wait for the user to invoke the task or action in your app that requires access to specific private user data. At that time, your app can request the runtime permission that is required for accessing that data.
Check whether the user has already granted
runtime permission your app requires. If so, your app can access the private user data. If not, continue to the next step.
You must check whether you have a permission every time you perform an operation that requires that permission.
Check whether your app should show a rationale to the user, explaining why your app needs the user to grant a particular runtime permission. If the system determines that your app shouldn't show a rationale, continue to the next step directly, without showing a UI element.
If the system determines that your app should show a rationale, however, present the rationale to the user in a UI element. In this rationale, clearly explain what data your app is trying to access and what benefits the app can provide to the user if they grant the runtime permission. After the user acknowledges the rationale, continue to the next step.
Request the runtime permission your app requires to access the private user data. The system displays a runtime permission prompt, such as the one shown on the permissions overview page.
Check the user's response---whether they chose to grant or deny the runtime permission.
If the user granted the permission to your app, you can access the private user data. If the user denied the permission instead, gracefully degrade your app experience so that it provides functionality to the user without the information that's protected by that permission.
Figure 1 illustrates the workflow and set of decisions associated with this process: Image: A flowchart illustrating the decision process for requesting runtime permissions, starting with an app action, checking if permission is granted, checking if rationale is needed, requesting permission, and handling the user's response. Figure 1. Diagram that shows the workflow for declaring and requesting runtime permissions on Android.
Determine whether your app was already granted the permission
To check whether the user already granted your app a particular permission, pass
that permission into the ContextCompat.checkSelfPermission() method.
This method returns either PERMISSION_GRANTED or
PERMISSION_DENIED, depending on whether your app has the permission.
Explain why your app needs the permission
The permissions dialog shown by the system when you call
requestPermissions() says what permission your app wants, but doesn't
say why. In some cases, the user might find that puzzling. It is a good idea to
explain to the user why your app wants the permissions before you call
requestPermissions().
Research shows that users are much more comfortable with permissions requests if they know why the app needs them, such as whether the permission is needed to support a core feature of the app or for advertising. As a result, if you are only using a fraction of the API calls that fall under a permission group, explicitly list which of those permissions you are using and why. For example, if you are only using coarse location, let the user know this in your app description or in help articles about your app.
Under certain conditions, it is also helpful to let users know about sensitive data access in real time. For example, if you are accessing the camera or microphone, consider letting the user know by using a notification icon somewhere in your app, or in the notification tray (if the application is running in the background), so it doesn't seem like you are collecting data surreptitiously.
[!NOTE] Note: Starting in Android 12 (API level 31), privacy indicators notify the user whenever applications access the microphone or camera.
If you need to request a permission for a feature to work, but the reason is not clear to the user, find a way to explain why you need sensitive permissions.
If the ContextCompat.checkSelfPermission() method returns PERMISSION_DENIED,
call shouldShowRequestPermissionRationale(). If this method returns
true, show an educational UI to the user. In this UI, describe why
the feature that the user wants to enable needs a particular permission.
Additionally, if your app requests a permission related to location, microphone, or camera, consider explaining why your app needs access to this information.
Request permissions
After the user views an educational UI, or the return value of
shouldShowRequestPermissionRationale() indicates that you don't need to show
an educational UI, request the permission. Users see a system permission
dialog, where they can choose whether to grant a particular permission to your
app.
To do this, use the RequestPermission contract, included in an AndroidX
library, where you allow the system to manage the permission request code
for you. Because using the RequestPermission contract simplifies your logic,
it is the recommended solution when possible. However, if needed you can also
manage a request code yourself as part of the permission request and
include this request code in your permission callback logic.
Allow the system to manage the permission request code
To allow the system to manage the request code that is associated with a
permissions request, add dependencies on the following libraries in your
module's build.gradle file:
androidx.activity, version 1.2.0 or laterandroidx.fragment, version 1.3.0 or later
You can then use one of the following classes:
To request a single permission, use
RequestPermission.To request multiple permissions at the same time, use
RequestMultiplePermissions.
The following steps show how to use the RequestPermission contract. The
process is nearly the same for the RequestMultiplePermissions contract.
In your activity or fragment's initialization logic, pass in an implementation of
ActivityResultCallbackinto a call toregisterForActivityResult(). TheActivityResultCallbackdefines how your app handles the user's response to the permission request.Keep a reference to the return value of
registerForActivityResult(), which is of typeActivityResultLauncher.To display the system permissions dialog when necessary, call the
launch()method on the instance ofActivityResultLauncherthat you saved in the previous step.After
launch()is called, the system permissions dialog appears. When the user makes a choice, the system asynchronously invokes your implementation ofActivityResultCallback, which you defined in the previous step. Note: Your app cannot customize the dialog that appears when you calllaunch(). To provide more information or context to the user, change your app's UI so that it's easier for users to understand why a feature in your app needs a particular permission. For example, you might change the text in the button that enables the feature.Also, the text in the system permission dialog references the permission group associated with the permission that you requested. This permission grouping is designed for system ease-of-use, and your app shouldn't rely on permissions being within or outside of a specific permission group.
The following code snippet shows how to handle the permissions response:
Kotlin
when {
ContextCompat.checkSelfPermission(
CONTEXT,
Manifest.permission.REQUESTED_PERMISSION
) == PackageManager.PERMISSION_GRANTED -> {
// You can use the API that requires the permission.
performAction(...)
}
ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.REQUESTED_PERMISSION) -> {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
showInContextUI(...)
}
else -> {
// You can directly ask for the permission.
requestPermissions(CONTEXT,
arrayOf(Manifest.permission.REQUESTED_PERMISSION),
REQUEST_CODE)
}
}Java
if (ContextCompat.checkSelfPermission(
CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) {
// You can use the API that requires the permission.
performAction(...);
} else if (ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.REQUESTED_PERMISSION)) {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
showInContextUI(...);
} else {
// You can directly ask for the permission.
requestPermissions(CONTEXT,
new String[] { Manifest.permission.REQUESTED_PERMISSION },
REQUEST_CODE);
}And this code snippet demonstrates the recommended process to check for a permission and to request a permission from the user when necessary:
Kotlin
when {
ContextCompat.checkSelfPermission(
CONTEXT,
Manifest.permission.REQUESTED_PERMISSION
) == PackageManager.PERMISSION_GRANTED -> {
// You can use the API that requires the permission.
}
ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.REQUESTED_PERMISSION) -> {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
showInContextUI(...)
}
else -> {
// You can directly ask for the permission.
// The registered ActivityResultCallback gets the result of this request.
requestPermissionLauncher.launch(
Manifest.permission.REQUESTED_PERMISSION)
}
}Java
if (ContextCompat.checkSelfPermission(
CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) {
// You can use the API that requires the permission.
performAction(...);
} else if (ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.REQUESTED_PERMISSION)) {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
showInContextUI(...);
} else {
// You can directly ask for the permission.
// The registered ActivityResultCallback gets the result of this request.
requestPermissionLauncher.launch(
Manifest.permission.REQUESTED_PERMISSION);
}Manage the permission request code yourself
As an alternative to allowing the system to manage the permission request
code, you can manage the permission request code yourself. To do so,
include the request code in a call to requestPermissions().
[!NOTE] Note: Your app cannot customize the dialog that appears when you call
requestPermissions(). The text in the system permission dialog references a permission group, but this permission grouping is designed for system ease-of-use. Your app must not rely on permissions being within or outside of a specific permission group.
The following code snippet demonstrates how to request a permission using a request code:
Kotlin
when {
ContextCompat.checkSelfPermission(
CONTEXT,
Manifest.permission.REQUESTED_PERMISSION
) == PackageManager.PERMISSION_GRANTED -> {
// You can use the API that requires the permission.
performAction(...)
}
ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.REQUESTED_PERMISSION) -> {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
showInContextUI(...)
}
else -> {
// You can directly ask for the permission.
requestPermissions(CONTEXT,
arrayOf(Manifest.permission.REQUESTED_PERMISSION),
REQUEST_CODE)
}
}Java
if (ContextCompat.checkSelfPermission(
CONTEXT, Manifest.permission.REQUESTED_PERMISSION) ==
PackageManager.PERMISSION_GRANTED) {
// You can use the API that requires the permission.
performAction(...);
} else if (ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.REQUESTED_PERMISSION)) {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
showInContextUI(...);
} else {
// You can directly ask for the permission.
requestPermissions(CONTEXT,
new String[] { Manifest.permission.REQUESTED_PERMISSION },
REQUEST_CODE);
}After the user responds to the system permissions dialog, the system then
invokes your app's implementation of onRequestPermissionsResult(). The
system passes in the user response to the permission dialog, as well as the
request code that you defined, as shown in the following code snippet:
Kotlin
override fun onRequestPermissionsResult(requestCode: Int,
permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
PERMISSION_REQUEST_CODE -> {
// If request is cancelled, the result arrays are empty.
if ((grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
// Permission is granted. Continue the action or workflow
// in your app.
} else {
// Explain to the user that the feature is unavailable because
// the feature requires a permission that the user has denied.
// At the same time, respect the user's decision. Don't link to
// system settings in an effort to convince the user to change
// their decision.
}
return
}
// Add other 'when' lines to check for other
// permissions this app might request.
else -> {
// Ignore all other requests.
}
}
}Java
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission is granted. Continue the action or workflow
// in your app.
} else {
// Explain to the user that the feature is unavailable because
// the feature requires a permission that the user has denied.
// At the same time, respect the user's decision. Don't link to
// system settings in an effort to convince the user to change
// their decision.
}
return;
}
// Other 'case' lines to check for other
// permissions this app might request.
}
}Request location permissions
When you request location permissions, follow the same best practices as for any other runtime permission. One important difference when it comes to location permissions is that the system includes multiple permissions related to location. Which permissions you request, and how you request them, depend on the location requirements for your app's use case.
Foreground location
If your app contains a feature that shares or receives location information only once, or for a defined amount of time, then that feature requires foreground location access. Some examples include the following:
Within a navigation app, a feature lets users get turn-by-turn directions.
Within a messaging app, a feature lets users share their current location with another user.
The system considers your app to be using foreground location if a feature of your app accesses the device's current location in one of the following situations:
An activity that belongs to your app is visible.
Your app is running a foreground service. When a foreground service is running, the system raises user awareness by showing a persistent notification. Your app retains access when it is placed in the background, such as when the user presses the Home button on their device or turns their device's display off.
On Android 10 (API level 29) and higher, you must declare a foreground service type of
location, as shown in the following code snippet. On earlier versions of Android, we recommend that you declare this foreground service type.<!-- Recommended for Android 9 (API level 28) and lower. --> <!-- Required for Android 10 (API level 29) and higher. --> <service android:name="MyNavigationService" android:foregroundServiceType="location" ... > <!-- Any inner elements go here. --> </service>
You declare a need for foreground location when your app requests either the
ACCESS_COARSE_LOCATION permission or the
ACCESS_FINE_LOCATION permission, as shown in the following snippet:
<manifest ... >
<!-- Include this permission any time your app needs location information. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Include only if your app benefits from precise location access. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>Background location
An app requires background location access if a feature within the app constantly shares location with other users or uses the Geofencing API. Several examples include the following:
Within a family location sharing app, a feature lets users continuously share location with family members.
Within an IoT app, a feature lets users configure their home devices such that they turn off when the user leaves their home and turn back on when the user returns home.
The system considers your app to be using background location if it accesses the device's current location in any situation other than the ones described in the foreground location section. The background location accuracy is the same as the foreground location accuracy, which depends on the location permissions that your app declares.
On Android 10 (API level 29) and higher, you must declare the
ACCESS_BACKGROUND_LOCATION permission in your app's manifest to request
background location access at runtime. On earlier versions of Android, when your
app receives foreground location access, it automatically receives background
location access as well.
<manifest ... >
<!-- Required only when requesting background location access on
Android 10 (API level 29) and higher. -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>[!NOTE] Note: The Google Play Store has a location policy concerning device location, restricting background location access to apps that need it for their core functionality and meet related policy requirements.
Handle permission denial
If the user denies a permission request, your app must help them understand the implications of denying the permission. In particular, your app must make users aware of the features that don't work because of the missing permission. When you do so, keep the following best practices in mind:
Minimize functionality loss. Users should be able to access the app to whatever extent is possible without the requested permissions. If functionality works at least to some extent with the data available, even if it is degraded compared to what would have been possible with the permission, keeping it on is the best option.
Guide the user's attention. Highlight a specific part of your app's UI where there is limited functionality because your app doesn't have the necessary permission. Examples of what you can do include the following:
Show a message where the feature's results or data would have appeared.
Display a different button that contains an error icon and color.
Be specific. Don't display a generic message. Instead, make clear which features are unavailable because your app doesn't have the necessary permission.
Don't block the user interface. In other words, don't display a full-screen warning message that prevents users from continuing to use your app at all.
Accept the user's preference: While it is okay to communicate to users how their functionality is being impacted by lack of access to the requested permissions, they should not feel pressured to change their mind. Letting a user know that they aren't getting full feature access when it is appropriate (such as when attempting to use a part of the app blocked without the permission) is important, but persistent nagging to reconsider is not respectful of their choice.
Following these guidelines can help ensure that users feel empowered to manage access to their private data in a way that aligns with their values. Failing to gracefully accept highly visible user privacy decisions damages the trust and reputation not only of your app, but of the ecosystem as a whole.
[!TIP] Tip: We recommend that your app encourages the best user experience possible, even after permission denials. For example, if microphone access is denied, you must still promote full usability of text functionality.
Additionally, if the user taps Deny for a specific permission more than once during your app's lifetime of installation on a device, the user will no longer see the system permissions dialog if your app requests that permission again. The user's action implies "don't ask again," and is considered a permanent denial. It is very important to only prompt users for permissions when they need access to a specific feature; otherwise, you might inadvertently lose the ability to re-request permissions. In certain situations, the permission might be denied automatically, without the user taking any action. (A permission might be granted automatically as well.) It is important to not assume anything about automatic behavior. Each time your app needs to access functionality that requires a permission, check that your app is still granted that permission. To provide the best user experience when asking for app permissions, also see App permissions best practices.
Inspect denial status when testing and debugging
To identify whether an app has been permanently denied permissions (for debugging and testing purposes), use the following command:
adb shell dumpsys package PACKAGE_NAMEWhere <var translate="no">PACKAGE_NAME</var> is the name of the package to inspect.
The output of the command contains sections that look like this:
...
runtime permissions:
android.permission.POST_NOTIFICATIONS: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
android.permission.ACCESS_FINE_LOCATION: granted=false, flags=[ USER_SET|USER_FIXED|USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
android.permission.BLUETOOTH_CONNECT: granted=false, flags=[ USER_SENSITIVE_WHEN_GRANTED|USER_SENSITIVE_WHEN_DENIED]
...Permissions that have been denied once by the user are flagged by USER_SET.
Permissions that have been denied permanently by selecting Deny twice are
flagged by USER_FIXED.
To make sure that testers see the request dialog during testing, reset these flags when you're done debugging your app. To do this, use the command:
adb shell pm clear-permission-flags PACKAGE_NAME PERMISSION_NAME user-set user-fixed<var translate="no">PERMISSION_NAME</var> is the name of the permission you want to reset.
PERMISSION_NAME is the name of the permission you want to reset.
To view a complete list of Android app permissions, visit the permissions API reference page.
One-time permissions
Image: The option called 'Only this time' is the second of three buttons in the dialog. Figure 2. System dialog that appears when an app requests a one-time permission.
Starting in Android 11 (API level 30), whenever your app requests a permission related to location, microphone, or camera, the user-facing permissions dialog contains an option called Only this time , as shown in figure 2. If the user selects this option in the dialog, your app is granted a temporary one-time permission.
Your app can then access the related data for a period of time that depends on your app's behavior and the user's actions:
While your app's activity is visible, your app can access the data.
If the user sends your app to the background, your app can continue to access the data for a short period of time.
If you launch a foreground service while the activity is visible, and the user then moves your app to the background, your app can continue to access the data until the foreground service stops.
App process terminates when permission revoked
If the user revokes the one-time permission, such as in system settings, your app cannot access the data, regardless of whether you launched a foreground service. As with any permission, if the user revokes your app's one-time permission, your app's process terminates.
When the user next opens your app and a feature in your app requests access to location, microphone, or camera, the user is prompted for the permission again.
[!NOTE] Note: If your app already follows best practices when it requests runtime permissions, you don't need to add or change any logic in your app to support one-time permissions.
Reset unused permissions
Android provides several ways to reset unused runtime permissions to their default, denied state:
An API where you can proactively remove your app's access to an unused runtime permission.
A system mechanism that automatically resets the permissions of unused apps.
Remove app access
On Android 13 (API level 33) and higher, you can remove your app's access to runtime permissions that your app no longer requires. When you update your app, perform this step so that users are more likely to understand why your app continues to request specific permissions. This knowledge helps build user trust in your app.
To remove access to a runtime permission, pass the name of that permission into
revokeSelfPermissionOnKill(). To remove access to a group of runtime
permissions at the same time, pass a collection of permission names into
revokeSelfPermissionsOnKill(). The permission removal process happens
asynchronously and kills all processes associated with your app's UID.
[!NOTE] Note: For system settings to show that your app doesn't access data in a particular permission group, you must remove access to all permissions in that permission group. In this case, it can be helpful to call
revokeSelfPermissionsOnKill()and pass in multiple permissions within the permission group.
For the system to remove your app's access to the permissions, all processes tied to your app must be killed. When you call the API, the system determines when it is safe to kill these processes. Usually, the system waits until your app spends an extended period of time running in the background instead of the foreground.
To inform the user that your app no longer requires access to specific runtime permissions, show a dialog the next time the user launches your app. This dialog can include the list of permissions.
Auto-reset permissions of unused apps
If your app targets Android 11 (API level 30) or higher and is not used for a few months, the system protects user data by automatically resetting the sensitive runtime permissions that the user had granted your app. Learn more in the guide about app hibernation.
Request to become the default handler if necessary
Some apps depend on access to sensitive user information related to call logs and SMS messages. If you want to request the permissions specific to call logs and SMS messages and publish your app to the Play Store, you must prompt the user to set your app as the default handler for a core system function before requesting these runtime permissions.
For more information on default handlers, including guidance on showing a default handler prompt to users, see the guide about permissions used only in default handlers.
Grant all runtime permissions for testing purposes
To grant all runtime permissions automatically when you install an app on an
emulator or test device, use the -g option for the adb shell install
command, as demonstrated in the following code snippet:
adb shell install -g PATH_TO_APK_FILEAdditional resources
For additional information about permissions, read these articles:
To learn more about requesting permissions, review the permissions samples.
You can also complete this codelab that demonstrates privacy best practices.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Google LLC
- Publisher
- Android (Google) (Official)
- Distributed by
- Android (Google)
- Source
- android/skills · security/android-permissions-security · commit bf68e2d
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.txt)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Google LLC or Android (Google).
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download android-permissions-security.zip