microg/GmsCore · error · RuntimeException
app op denied
Error message
app op denied
What it means
When delivering each location update, the manager calls noteAppOpForEffectiveGranularity to record the corresponding app op (e.g._FINE_LOCATION/_COARSE_LOCATION) for the client. If the app op is denied (user restricted location for the app, e.g. 'Allow only while in use', or app op set to ignore/errored), the update cannot be attributed and a RuntimeException 'app op denied' is thrown.
Source
Thrown at play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt:471
fun check() {
if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException("Lack of permission")
if (effectiveGranularity > permissionGranularity) throw RuntimeException("Lack of permission")
if (timePendingMillis < 0) throw RuntimeException("duration limit reached (active for ${(SystemClock.elapsedRealtime() - start).formatDuration()}, duration ${request.durationMillis.formatDuration()})")
if (updatesPending <= 0) throw RuntimeException("max updates reached")
if (callback?.asBinder()?.isBinderAlive == false) throw RuntimeException("Binder died")
}
fun processNewLocation(location: Location): Boolean {
check()
if (lastLocation != null && location.elapsedMillis - lastLocation!!.elapsedMillis < request.minUpdateIntervalMillis) return false
if (lastLocation != null && location.distanceTo(lastLocation!!) < request.minUpdateDistanceMeters) return false
if (lastLocation == location) return false
val returnedLocation = if (effectiveGranularity > permissionGranularity) {
throw RuntimeException("Lack of permission")
} else {
if (!context.noteAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) {
throw RuntimeException("app op denied")
} else if (clientIdentity.isSelfProcess()) {
Location(location)
} else {
Location(location).apply { provider = "fused" }
}
}
val result = LocationResult.create(listOf(returnedLocation))
callback?.onLocationResult(result)
pendingIntent?.send(context, 0, Intent().apply { putExtra(LocationResult.EXTRA_LOCATION_RESULT, result) })
if (request.maxUpdates != Int.MAX_VALUE) updates++
check()
return true
}
init {
require(callback != null || pendingIntent != null)
}
}View on GitHub (pinned to 157c9d86ac)
Solutions
- Re-request location permission / ask the user to re-enable location for the app in Settings
- Remove and re-register the location request after permission state changes so the app op is re-evaluated
- Check permission and app-op state (ContextCompat.checkSelfPermission + AppOpsManager) before and during updates
- Fall back to coarse updates or stop requesting updates when the app op is denied
Example fix
// before
client.requestLocationUpdates(request, listener, looper) // never re-checked after denial
// after
if (ContextCompat.checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == PERMISSION_GRANTED &&
appOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION)) {
client.requestLocationUpdates(request, listener, looper)
} Defensive patterns
Strategy: validation
Validate before calling
fun locationAppOpAllowed(ctx: Context): Boolean {
val ops = ctx.getSystemService(AppOpsManager::class.java)
val mode = ops.noteOpNoThrow(AppOpsManager.OPSTR_FINE_LOCATION, android.os.Process.myUid(), ctx.packageName)
return mode == AppOpsManager.MODE_ALLOWED
} Type guard
fun canReceiveUpdates(ctx: Context) =
ContextCompat.checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
locationAppOpAllowed(ctx) Try / catch
try {
client.requestLocationUpdates(request, listener, looper)
} catch (e: RuntimeException) {
if (e.message == "app op denied") {
stopLocationUpdates(); showPermissionRationale()
} else throw e
} Prevention
- Check both runtime permission and AppOps mode before registering updates
- Listen for permission/app-op changes and unregister listeners when denied
- Guide users to Settings when location is restricted for the app
- Fall back gracefully to coarse or no updates when the op is denied
When it happens
Trigger: A location update arrives for a client whose location app op is currently denied in AppOps (user turned off location permission for the app in Settings, or a device-owner/profile-owner restriction denies it) while the request is still registered.
Common situations: User switches the app's location permission to 'Deny' or battery-restricted mode without the app removing its listener; work-profile or enterprise policy blocking location; OEM-level app op restrictions.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Caller must hold $permission for location bypass
- Access denied, missing google package permission or GET_ACCO
- invalid radius:
- invalid latitude:
- invalid longitude:
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/c84867ca57c5c299.
Report an issue: GitHub.