| 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 |
| 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 |
| 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. |