Tencent/matrix · error · IllegalStateException

Can not get ClassLoader of

Error message

Can not get ClassLoader of ${serviceManagerCls.getName()}

What it means

SystemServiceBinderHooker.createProxyBinder reflectively loads android.os.ServiceManager and builds a java.lang.reflect.Proxy over IBinder; Proxy.newProxyInstance requires a non-null ClassLoader. If ServiceManager's classloader is somehow null it throws IllegalStateException — essentially an impossible-on-stock-Android internal invariant guard for the hook's dynamic-proxy setup.

Solutions

  1. Fall back to the app/system ClassLoader: use IBinder.class.getClassLoader() instead of ServiceManager's when null.
  2. Skip the binder hook on this device and degrade gracefully (battery canary works without hooking).
  3. If running under Robolectric/unit tests, avoid calling the hooker — ServiceManager is a stub there.
  4. Report the ROM/runtime; this path indicates an environment the library doesn't support.

Example fix

// before
ClassLoader classLoader = serviceManagerCls.getClassLoader();
if (classLoader == null) {
    throw new IllegalStateException("Can not get ClassLoader of " + serviceManagerCls.getName());
}
// after
ClassLoader classLoader = serviceManagerCls.getClassLoader();
if (classLoader == null) {
    classLoader = IBinder.class.getClassLoader();
}
if (classLoader == null) {
    return originBinder; // skip hook
}
Defensive patterns

Strategy: try-catch

Validate before calling

try { Class<?> cls = Class.forName("android.os.ServiceManager"); if (cls.getClassLoader() == null) skipHook(); } catch (Throwable t) { skipHook(); }

Type guard

static boolean canHookServiceManager() {
    try { return Class.forName("android.os.ServiceManager").getClassLoader() != null; }
    catch (Throwable t) { return false; }
}

Try / catch

try {
    IBinder proxy = hooker.createProxyBinder();
} catch (Exception e) {
    Log.w(TAG, "binder hook unavailable, continuing un-hooked", e);
    proxy = originBinder;
}

Prevention

When it happens

Trigger: Calling createProxyBinder (via delegateBinder) on an environment where Class.forName("android.os.ServiceManager").getClassLoader() returns null — theoretically a bootstrap-class scenario or heavily modified/hooked runtime (custom Xposed-like frameworks, exotic ART builds).

Common situations: Instrumentation/hook frameworks altering bootstrap classloading; running on non-standard JVM/Robolectric test environments where android.os.ServiceManager is mocked; heavily customized OEM ROMs.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08). Data as JSON: /api/errors/d38f2c5c25f84b8f. Report an issue: GitHub.

Appendix: source

Thrown at matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/SystemServiceBinderHooker.java:138

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("queryLocalInterface".equals(method.getName())) {
                return mServiceManagerProxy;
            }
            return method.invoke(mOriginBinder, args);
        }

        public IBinder getOriginBinder() {
            return mOriginBinder;
        }

        @SuppressWarnings({"PrivateApi"})
        public IBinder createProxyBinder() throws Exception  {
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            ClassLoader classLoader = serviceManagerCls.getClassLoader();
            if (classLoader == null) {
                throw new IllegalStateException("Can not get ClassLoader of " + serviceManagerCls.getName());
            }
            return (IBinder) Proxy.newProxyInstance(
                    classLoader,
                    new Class<?>[]{IBinder.class},
                    this
            );
        }

        @SuppressWarnings({"PrivateApi"})
        static IBinder getCurrentBinder(String serviceName) throws Exception {
            Class<?> serviceManagerCls = Class.forName("android.os.ServiceManager");
            Method getService = serviceManagerCls.getDeclaredMethod("getService", String.class);
            return  (IBinder) getService.invoke(null, serviceName);
        }

        @SuppressWarnings({"PrivateApi"})
        private static Object createServiceManagerProxy(String serviceClassName, IBinder originBinder, final HookCallback callback) throws Exception  {
            Class<?> serviceManagerCls = Class.forName(serviceClassName);

View on GitHub (pinned to 3b8293bd65)