Tencent/tinker · error · IllegalStateException
fakeBinder does not return fakeInterface, binder: {}, itf: {
Error message
fakeBinder does not return fakeInterface, binder: {}, itf: {} What it means
After installing the fake binder proxy, Tinker fixes the cached AMS interface: it replaces Singleton.mInstance (e.g. IActivityManagerSingleton.mInstance) with fakeBinder.queryLocalInterface(...). This error means queryLocalInterface returned null or an object that is not a Tinker proxy (does not implement ITinkerHotplugProxy), so the cache cannot be swapped safely.
Source
Thrown at tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor.java:113
final Class<?> amsNativeClazz = Class.forName("android.app.ActivityManagerNative");
final Field gDefaultField = ShareReflectUtil.findField(amsNativeClazz, "gDefault");
singletonObj = gDefaultField.get(null);
} catch (Throwable thr) {
final Class<?> amClazz = Class.forName("android.app.ActivityManager");
final Field iActivityManagerSingletonField = ShareReflectUtil.findField(amClazz, "IActivityManagerSingleton");
singletonObj = iActivityManagerSingletonField.get(null);
}
final Field mInstanceField = ShareReflectUtil.findField(singletonObj, "mInstance");
final IInterface originalInterface = (IInterface) mInstanceField.get(singletonObj);
if (originalInterface == null || ITinkerHotplugProxy.class.isAssignableFrom(originalInterface.getClass())) {
return;
}
final IInterface fakeInterface = fakeBinder.queryLocalInterface(fakeBinder.getInterfaceDescriptor());
if (fakeInterface == null || !ITinkerHotplugProxy.class.isAssignableFrom(fakeInterface.getClass())) {
throw new IllegalStateException("fakeBinder does not return fakeInterface, binder: " + fakeBinder + ", itf: " + fakeInterface);
}
mInstanceField.set(singletonObj, fakeInterface);
}
private static void fixPMSBinderCache(Context context, IBinder fakeBinder) throws Throwable {
final Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
final Field sPackageManagerField = ShareReflectUtil.findField(activityThreadClazz, "sPackageManager");
final IInterface originalInterface = (IInterface) sPackageManagerField.get(null);
if (originalInterface != null && !ITinkerHotplugProxy.class.isAssignableFrom(originalInterface.getClass())) {
final IInterface fakeInterface = fakeBinder.queryLocalInterface(fakeBinder.getInterfaceDescriptor());
if (fakeInterface == null || !ITinkerHotplugProxy.class.isAssignableFrom(fakeInterface.getClass())) {
throw new IllegalStateException("fakeBinder does not return fakeInterface, binder: " + fakeBinder + ", itf: " + fakeInterface);
}
sPackageManagerField.set(null, fakeInterface);
}
final Class<?> applicationPackageManagerClazz = Class.forName("android.app.ApplicationPackageManager");
final Field mPMField = ShareReflectUtil.findField(applicationPackageManagerClazz, "mPM");View on GitHub (pinned to 1b7ea02c23)
Solutions
- Update Tinker — newer FakeClientBinder implementations register the proxy under all expected descriptors.
- Ensure ComponentHotplug.install is called once per process (uninstall before re-install).
- Catch the exception and disable component hotplug on that ROM rather than crashing startup.
Defensive patterns
Strategy: try-catch
Validate before calling
IInterface itf = fakeBinder.queryLocalInterface(fakeBinder.getInterfaceDescriptor());
if (itf == null || !(itf instanceof ITinkerHotplugProxy)) {
// proxy not registered under this descriptor: skip cache fix
} Type guard
private static boolean isTinkerProxy(Object o) {
return o != null && o instanceof ITinkerHotplugProxy;
} Try / catch
try {
fixAMSBinderCache(context, fakeBinder);
} catch (IllegalStateException e) {
// descriptor mismatch on this ROM: uninstall interceptor, run without hotplug
ComponentHotplug.uninstall();
} Prevention
- Install the interceptor exactly once per process; uninstall before reinstalling.
- Update Tinker for OEM descriptor variations.
- Always guard ComponentHotplug.install with UnsupportedEnvironmentException handling.
When it happens
Trigger: The fake binder's queryLocalInterface not returning the proxy created in createProxy — typically a descriptor mismatch between fakeBinder.getInterfaceDescriptor() and the descriptor the proxy was registered under, or the proxy not being cached in the binder's local-interface map.
Common situations: ROMs whose AMS descriptor differs from AOSP; version drift between the interceptor's FakeClientBinder implementation and the framework; double-install where the first install left inconsistent state.
Related errors
- cannot find class: {}
- Cannot query transaction code of performDexOptSecondary.
- Binder transaction failure.
- failed to fetch instance of ActivityThread.
- unexpected xml parser state when parsing incremental compone
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/aa9eead005b99ba5.
Report an issue: GitHub.