{"record":{"id":"45ef8f3bfcf03f75","repo":"microg/GmsCore","slug":"lack-of-permission","errorCode":null,"errorMessage":"Lack of permission","messagePattern":"Lack of permission","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt","lineNumber":436,"sourceCode":"                get() = request.intervalMillis\n            val updatesPending: Int\n                get() = request.maxUpdates - updates\n            val timePendingMillis: Long\n                get() = request.durationMillis - (SystemClock.elapsedRealtime() - start)\n            var workSource: WorkSource = WorkSource(request.workSource).also { if (!clientIdentity.isSelfUser()) WorkSourceUtil.add(it, clientIdentity.uid, clientIdentity.packageName) }\n                private set\n            val effectiveHighPower: Boolean\n                get() = request.intervalMillis < 60000 || effectivePriority == PRIORITY_HIGH_ACCURACY\n\n            fun update(callback: ILocationCallback, request: LocationRequest): LocationRequestHolder {\n                val changedGranularity = request.granularity != this.request.granularity || request.granularity == GRANULARITY_PERMISSION_LEVEL\n                this.callback = callback\n                this.request = request\n                this.start = SystemClock.elapsedRealtime()\n                this.updates = 0\n                this.workSource = WorkSource(request.workSource).also { if (!clientIdentity.isSelfUser()) WorkSourceUtil.add(it, clientIdentity.uid, clientIdentity.packageName) }\n                if (changedGranularity) {\n                    if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException(\"Lack of permission\")\n                }\n                return this\n            }\n\n            fun start(): LocationRequestHolder {\n                if (!context.checkAppOpForEffectiveGranularity(clientIdentity, effectiveGranularity)) throw RuntimeException(\"Lack of permission\")\n                return this\n            }\n\n            fun cancel() {\n                try {\n                    callback?.cancel()\n                } catch (e: Exception) {\n                    Log.w(TAG, e)\n                }\n            }\n\n            fun check() {","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt#L418-L454","documentation":"LocationRequestManager.LocationRequestHolder's init/update path throws RuntimeException('Lack of permission') when the app-op check for the request's effective granularity fails — the client does not hold the app-op (runtime appop mode denied) required for the granularity (e.g. precise/coarse) it requested. Unlike SecurityException paths, this surfaces as a RuntimeException inside request registration.","triggerScenarios":"Registering a location request (holder construction after changedGranularity) when checkAppOpForEffectiveGranularity returns false — e.g. the app has ACCESS_FINE_LOCATION declared but the system app-op for fine location is denied, or the request granularity exceeds what the app-op allows.","commonSituations":"User revoked location via AppOps (adb shell appops set <pkg> FINE_LOCATION deny) while the app keeps requesting; ROMs where runtime grant and app-op state diverge; microG self-check mismatch after permission upgrades.","solutions":["Re-grant the location permission/app-op: Settings > Apps > Permissions > Location, or adb shell appops set <pkg> ACCESS_FINE_LOCATION allow.","Lower the requested granularity (use COARSE granularity) to match the permission actually held.","Before requesting updates, verify checkSelfPermission and the corresponding app-op mode for fine location."],"exampleFix":"// before\nrequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 1000).build() // needs fine app-op\n// after\n// use coarse when only coarse is granted\nrequest = LocationRequest.Builder(Priority.PRIORITY_BALANCED_POWER_ACCURACY, 1000)\n    .setGranularity(Granularity.GRANULARITY_COARSE)\n    .build()","handlingStrategy":"validation","validationCode":"val fine = ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PERMISSION_GRANTED\nval coarse = ContextCompat.checkSelfPermission(context, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED\nval granularity = if (fine) Granularity.GRANULARITY_FINE else if (coarse) Granularity.GRANULARITY_COARSE else return","typeGuard":"fun granularityAllowed(ctx: Context, want: Int): Boolean =\n    when (want) {\n        Granularity.GRANULARITY_FINE -> ctx.checkSelfPermission(ACCESS_FINE_LOCATION) == PERMISSION_GRANTED\n        Granularity.GRANULARITY_COARSE -> ctx.checkSelfPermission(ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED\n        else -> true\n    }","tryCatchPattern":"try {\n    client.requestLocationUpdates(request, listener, looper)\n} catch (e: RuntimeException) {\n    if (e.message == \"Lack of permission\") {\n        downgradeToCoarseRequest(); promptForPermission()\n    }\n}","preventionTips":["Match request granularity to actually granted permissions at call time","Re-check permissions on every onResume before requesting updates","Avoid adb appops deny on packages that actively use location"],"tags":["location","appops","permission","granularity"],"backgroundTag":"insufficient-permissions","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}