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
- Fall back to the app/system ClassLoader: use IBinder.class.getClassLoader() instead of ServiceManager's when null.
- Skip the binder hook on this device and degrade gracefully (battery canary works without hooking).
- If running under Robolectric/unit tests, avoid calling the hooker — ServiceManager is a stub there.
- 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
- Treat binder hooking as best-effort; always fall back to the original binder.
- Skip hooks under Robolectric/unit-test environments.
- Log ROM/build fingerprint when the hook fails to spot device-specific incompatibilities.
- Don't let hook failure crash the battery monitor — degrade to un-hooked mode.
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
- get service manager ClassLoader fail!
- Both of invoker and fieldName can not be null or nil.
- unable to cast object
- Method is not exists.
- Both of invoker and fieldName can not be null or nil.
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)