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

  1. Update Tinker — newer FakeClientBinder implementations register the proxy under all expected descriptors.
  2. Ensure ComponentHotplug.install is called once per process (uninstall before re-install).
  3. 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

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


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/aa9eead005b99ba5. Report an issue: GitHub.