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://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
LICENSE
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.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