4

I am trying to access a method using reflection and one of the parameter is a callback. The callback type is generic interface of different class type. These classes are @SystemApi can be accessed by reflection. Here is the class I am working with above sayings.

Below is my sample code :

 String sClassName = "android.telephony.euicc.EuiccCardManager";
 Class classToInvestigate = Class.forName(sClassName);
 Class interfaceclass = classToInvestigate.getClasses()[0]; //android.telephony.euicc.EuiccCardManager.ResultCallback

 Method getallprofiles = classToInvestigate.getDeclaredMethod("requestAllProfiles", String.class,Executor.class, interfaceclass);
 getallprofiles.invoke(null,getEid(), AsyncTask.THREAD_POOL_EXECUTOR,null);

In the above invoke signature as a last parameter I need to pass the callback created using reflection and which is equivalent or similar to the below callback sample.

ResultCallback<EuiccProfileInfo[]> callback =
new ResultCallback<EuiccProfileInfo[]>() {
@Override
public void onComplete(int resultCode, EuiccProfileInfo[] result) { }
};
  • The above ResultCallback interface can be found in the same class link I provided above and the same instance in interfaceclass field above.
  • EuiccProfileInfo is the another class need to access from reflection because it is @SystemApi

I have failed/stuck to translate the above callback logic with reflection.Anyone can help me on this?

Prabhakaran
  • 1,264
  • 2
  • 20
  • 47

1 Answers1

4

Seems that you need to create a proxy class:

// that class you don't have access too. imagine that it is not here, it is just to show example signature of method we want to run.
interface Callback {
    void run();
}

public static void main(String[] args) throws Exception {
    Class<?> callbackClass = Class.forName("package.Callback");
    Callback callback = Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{callbackClass}, new CallbackInvocationHandler(() -> {
        System.out.println("callback");
    }));
    callbackClass.getMethod("run").invoke(callback); // works!
}

static class CallbackInvocationHandler implements InvocationHandler {
    private final Runnable myCallback;

    CallbackInvocationHandler(Runnable myCallback) {
        this.myCallback = myCallback;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("toString") && (method.getParameterCount() == 0)) {
            // just random implementation
            return proxy.getClass() + "@" + Integer.toHexString(System.identityHashCode(proxy));
        }
        if (method.getName().equals("hashCode") && (method.getParameterCount() == 0)) {
            return System.identityHashCode(proxy);
        }
        if (method.getName().equals("equals") && (method.getParameterCount() == 1) && (method.getParameterTypes()[0] == Object.class)) {
            return proxy == args[0];
        }
        if (method.getName().equals("run") && (method.getParameterCount() == 0)) {
            // do what you want.
            myCallback.run();
            return null;
        }
        throw new IllegalStateException("Method not implemented: " + method);
    }
}

But if you really need to use something like that - there are huge chances you are doing something wrong, can't you just add dependency on that project? But you should not depend on system classes too.

GotoFinal
  • 3,585
  • 2
  • 18
  • 33
  • Thanks for the answer..I took this as a base idea to write for my requirement. Proxy.newProxyInstance(interfaceclass.getClassLoader(), new Class[]{interfaceclass}, new ProxyListener()); public class ProxyListener implements java.lang.reflect.InvocationHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ProxyListener.class); @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { return null; }} – Prabhakaran Jul 20 '18 at 06:46
  • ```Callback``` in your example represents the hidden interface that you want to implement, right? Which you would get through ```Class> cCallback = Class.for("...")```. However, how are you supposed to do the cast in the end if you don't have the type itself. If you meant it in the way that you declare the hidden interface yourself as in your example, then still the proxy returned is null. – phoebus Aug 02 '18 at 17:55
  • @phoebus OP wanted to pass this callback using reflections to other method - so all he needs is an `Object` I used real type just to show working example. Also you can call this method via reflections if you need. – GotoFinal Aug 02 '18 at 18:05
  • I've created another question so you can see what exactly I want to achieve and how I want to use it: https://stackoverflow.com/questions/51659917/implement-hidden-android-interface-through-reflection-and-proxy – phoebus Aug 02 '18 at 18:16
  • @phoebus I modified my answer so it does not reference that example calback class directly – GotoFinal Aug 02 '18 at 18:27