microg/GmsCore · error · IllegalAccessException

DroidGuard should not be available locally

Error message

DroidGuard should not be available locally

What it means

microG's DroidGuard NetworkHandleProxyFactory.createHandle() refuses to build a local guard handle when DroidGuardPreferences.isLocalAvailable(context) is false. Local DroidGuard execution (bytecode from DB or fetched from server, run on-device) is an opt-in/conditional capability; when it is not available the factory throws IllegalAccessException instead of silently downgrading. The same guard exists in createPingHandle() and createLowLatencyHandle().

Solutions

  1. Enable local DroidGuard execution in the microG settings app (DroidGuard section) and retry
  2. Verify DroidGuardPreferences.isLocalAvailable(context) before calling createHandle and route to a remote handle (RemoteHandleImpl with a configured network server URL) as a fallback
  3. Update microG to a version that supports local DroidGuard on your device fingerprint
  4. Catch IllegalAccessException around handle creation and surface a 'local DroidGuard unavailable' state to the caller

Example fix

// before
val handle = factory.createHandle(packageName, flow, callback, request)

// after
if (DroidGuardPreferences.isLocalAvailable(context)) {
    val handle = factory.createHandle(packageName, flow, callback, request)
} else {
    val handle = RemoteHandleImpl(context, packageName).also { it.init(flow) }
}
Defensive patterns

Strategy: validation

Validate before calling

if (!DroidGuardPreferences.isLocalAvailable(context)) {
    // fall back to remote handle or disable feature
} else {
    val handle = factory.createHandle(packageName, flow, callback, request)
}

Try / catch

try {
    factory.createHandle(packageName, flow, callback, request)
} catch (e: IllegalAccessException) {
    switchToRemoteHandle(context, packageName, flow)
}

Prevention

When it happens

Trigger: Calling createHandle(packageName, flow, callback, request) on a device where DroidGuardPreferences.isLocalAvailable(context) returns false — i.e. local DroidGuard execution is disabled in microG settings or the device/build is not provisioned for local execution.

Common situations: User has not enabled 'DroidGuard local execution' in microG settings; a custom ROM or unsupported Build.FINGERPRINT lacks local DroidGuard support; an app integrates the DroidGuard API assuming local handling while the install is remote-only.

Related errors


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/54fcf43f85657f1b. Report an issue: GitHub.

Appendix: source

Thrown at play-services-droidguard/core/src/main/kotlin/org/microg/gms/droidguard/core/NetworkHandleProxyFactory.kt:31

import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
import okio.ByteString.Companion.decodeHex
import okio.ByteString.Companion.of
import org.microg.gms.droidguard.*
import org.microg.gms.profile.Build
import org.microg.gms.profile.ProfileManager
import org.microg.gms.utils.singleInstanceOf
import java.io.File
import java.util.*
import com.android.volley.Request as VolleyRequest
import com.android.volley.Response as VolleyResponse

class NetworkHandleProxyFactory(private val context: Context) : HandleProxyFactory(context) {
    private val dgDb: DgDatabaseHelper = DgDatabaseHelper(context)
    private val version = VersionUtil(context)
    private val queue = singleInstanceOf { Volley.newRequestQueue(context.applicationContext) }

    fun createHandle(packageName: String, flow: String?, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {
        if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException("DroidGuard should not be available locally")
        val (vmKey, byteCode, bytes) = readFromDatabase(flow) ?: fetchFromServer(flow, packageName)
        return createHandleProxy(flow, vmKey, byteCode, bytes, callback, request)
    }

    fun createPingHandle(packageName: String, flow: String, callback: GuardCallback, pingData: PingData?): HandleProxy {
        if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException("DroidGuard should not be available locally")
        val (vmKey, byteCode, bytes) = fetchFromServer(flow, createRequest(flow, packageName, pingData))
        return createHandleProxy(flow, vmKey, byteCode, bytes, callback, DroidGuardResultsRequest().also { it.clientVersion = 0 })
    }

    fun createLowLatencyHandle(flow: String?, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {
        if (!DroidGuardPreferences.isLocalAvailable(context)) throw IllegalAccessException("DroidGuard should not be available locally")
        val (vmKey, byteCode, bytes) = readFromDatabase("fast") ?: throw Exception("low latency (fast) flow not available")
        return createHandleProxy(flow, vmKey, byteCode, bytes, callback, request)
    }

    fun SignedResponse.unpack(): Response {
        if (SignatureVerifier.verifySignature(data_!!.toByteArray(), signature!!.toByteArray())) {

View on GitHub (pinned to 157c9d86ac)