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

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)

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.