{"record":{"id":"d38f2c5c25f84b8f","repo":"Tencent/matrix","slug":"can-not-get-classloader-of-servicemanagercls-get","errorCode":null,"errorMessage":"Can not get ClassLoader of ${serviceManagerCls.getName()}","messagePattern":"Can not get ClassLoader of (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/SystemServiceBinderHooker.java","lineNumber":138,"sourceCode":"\n        @Override\n        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n            if (\"queryLocalInterface\".equals(method.getName())) {\n                return mServiceManagerProxy;\n            }\n            return method.invoke(mOriginBinder, args);\n        }\n\n        public IBinder getOriginBinder() {\n            return mOriginBinder;\n        }\n\n        @SuppressWarnings({\"PrivateApi\"})\n        public IBinder createProxyBinder() throws Exception  {\n            Class<?> serviceManagerCls = Class.forName(\"android.os.ServiceManager\");\n            ClassLoader classLoader = serviceManagerCls.getClassLoader();\n            if (classLoader == null) {\n                throw new IllegalStateException(\"Can not get ClassLoader of \" + serviceManagerCls.getName());\n            }\n            return (IBinder) Proxy.newProxyInstance(\n                    classLoader,\n                    new Class<?>[]{IBinder.class},\n                    this\n            );\n        }\n\n        @SuppressWarnings({\"PrivateApi\"})\n        static IBinder getCurrentBinder(String serviceName) throws Exception {\n            Class<?> serviceManagerCls = Class.forName(\"android.os.ServiceManager\");\n            Method getService = serviceManagerCls.getDeclaredMethod(\"getService\", String.class);\n            return  (IBinder) getService.invoke(null, serviceName);\n        }\n\n        @SuppressWarnings({\"PrivateApi\"})\n        private static Object createServiceManagerProxy(String serviceClassName, IBinder originBinder, final HookCallback callback) throws Exception  {\n            Class<?> serviceManagerCls = Class.forName(serviceClassName);","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/SystemServiceBinderHooker.java#L120-L156","documentation":"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.","triggerScenarios":"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).","commonSituations":"Instrumentation/hook frameworks altering bootstrap classloading; running on non-standard JVM/Robolectric test environments where android.os.ServiceManager is mocked; heavily customized OEM ROMs.","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."],"exampleFix":"// before\nClassLoader classLoader = serviceManagerCls.getClassLoader();\nif (classLoader == null) {\n    throw new IllegalStateException(\"Can not get ClassLoader of \" + serviceManagerCls.getName());\n}\n// after\nClassLoader classLoader = serviceManagerCls.getClassLoader();\nif (classLoader == null) {\n    classLoader = IBinder.class.getClassLoader();\n}\nif (classLoader == null) {\n    return originBinder; // skip hook\n}","handlingStrategy":"try-catch","validationCode":"try { Class<?> cls = Class.forName(\"android.os.ServiceManager\"); if (cls.getClassLoader() == null) skipHook(); } catch (Throwable t) { skipHook(); }","typeGuard":"static boolean canHookServiceManager() {\n    try { return Class.forName(\"android.os.ServiceManager\").getClassLoader() != null; }\n    catch (Throwable t) { return false; }\n}","tryCatchPattern":"try {\n    IBinder proxy = hooker.createProxyBinder();\n} catch (Exception e) {\n    Log.w(TAG, \"binder hook unavailable, continuing un-hooked\", e);\n    proxy = originBinder;\n}","preventionTips":["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."],"tags":["android","reflection","dynamic-proxy","hook"],"backgroundTag":"internal-invariant-violation","analyzedSha":"3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7","analyzedAt":"2026-09-08T08:01:39.722Z","contentChangedAt":"2026-09-08T08:01:39.722Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}