{"record":{"id":"847b51ecf38ef154","repo":"Tencent/matrix","slug":"get-service-manager-classloader-fail","errorCode":null,"errorMessage":"get service manager ClassLoader fail!","messagePattern":"get service manager ClassLoader fail!","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":160,"sourceCode":"                    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);\n            Class<?> serviceManagerStubCls = Class.forName(serviceClassName + \"$Stub\");\n            ClassLoader classLoader = serviceManagerStubCls.getClassLoader();\n            if (classLoader == null) {\n                throw new IllegalStateException(\"get service manager ClassLoader fail!\");\n            }\n            Method asInterfaceMethod = serviceManagerStubCls.getDeclaredMethod(\"asInterface\", IBinder.class);\n            final Object originManagerService = asInterfaceMethod.invoke(null, originBinder);\n            return Proxy.newProxyInstance(classLoader,\n                    new Class[]{IBinder.class, IInterface.class, serviceManagerCls},\n                    new InvocationHandler() {\n                        @Override\n                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n                            if (callback != null) {\n                                callback.onServiceMethodInvoke(method, args);\n                                Object result = callback.onServiceMethodIntercept(originManagerService, method, args);\n                                if (result != null) {\n                                    return result;\n                                }\n                            }\n                            return method.invoke(originManagerService, args);\n                        }\n                    }","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/SystemServiceBinderHooker.java#L142-L178","documentation":"createServiceManagerProxy loads a system service class and its $Stub inner class, then needs the Stub's ClassLoader to build a dynamic proxy implementing IBinder/IInterface/the service interface. A null ClassLoader would make Proxy.newProxyInstance fail, so it throws IllegalStateException 'get service manager ClassLoader fail!' as an environment-invariant guard.","triggerScenarios":"Hooking a system service where Class.forName(serviceClassName + \"$Stub\").getClassLoader() returns null — serviceClassName pointing to a wrong/hidden service class, bootstrap-classloader services on modified runtimes, or mock environments (Robolectric) where Stub classes behave differently.","commonSituations":"Passing an incorrect serviceClassName (typo or service absent on that API level); OEM ROMs moving services to different classloaders; hooking frameworks that relocate classes.","solutions":["Verify serviceClassName is correct for the target API level (e.g. android.app.INotificationManager vs older names); a wrong class name yields a bogus Stub.","Fall back to the caller's ClassLoader (SystemServiceBinderHooker.class.getClassLoader() or IBinder.class.getClassLoader()) when null.","Guard the hook with a try-catch and skip hooking on devices where it fails; the library degrades to un-hooked monitoring.","Check that the service actually exists before hooking (ServiceManager.getService != null)."],"exampleFix":"// before\nClassLoader classLoader = serviceManagerStubCls.getClassLoader();\nif (classLoader == null) {\n    throw new IllegalStateException(\"get service manager ClassLoader fail!\");\n}\n// after\nClassLoader classLoader = serviceManagerStubCls.getClassLoader();\nif (classLoader == null) {\n    classLoader = SystemServiceBinderHooker.class.getClassLoader();\n}\nif (classLoader == null) {\n    return originBinder; // skip hook\n}","handlingStrategy":"try-catch","validationCode":"if (serviceClassName == null || !serviceClassName.contains(\"$Stub\") == false) {\n    try { Class.forName(serviceClassName + \"$Stub\"); } catch (Throwable t) { return originBinder; }\n}","typeGuard":"static boolean serviceStubExists(String serviceClassName) {\n    try { Class.forName(serviceClassName + \"$Stub\"); return true; }\n    catch (Throwable t) { return false; }\n}","tryCatchPattern":"try {\n    Object proxy = createServiceManagerProxy(serviceClassName, originBinder, callback);\n} catch (Exception e) {\n    Log.w(TAG, \"service hook failed for \" + serviceClassName + \", skipping\", e);\n    proxy = originBinder;\n}","preventionTips":["Verify the service class and its $Stub exist on the target API level before hooking.","Check ServiceManager.getService(name) != null before attempting the hook.","Wrap every hook attempt in try-catch with fallback to the un-hooked binder.","Test on min-API and max-API devices; service class names change across Android versions."],"tags":["android","reflection","dynamic-proxy","system-service","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"}