blob: 51c78ed36acb9a436eb3695cee7e1c876bb2a327 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.support_lib_boundary.util;
import android.annotation.TargetApi;
import android.os.Build;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* A set of utility methods used for calling across the support library boundary.
*/
public class BoundaryInterfaceReflectionUtil {
/**
* Utility method for fetching a method from {@param delegateLoader}, with the same signature
* (package + class + method name + parameters) as a given method defined in another
* classloader.
*/
public static Method dupeMethod(Method method, ClassLoader delegateLoader)
throws ClassNotFoundException, NoSuchMethodException {
Class<?> declaringClass =
Class.forName(method.getDeclaringClass().getName(), true, delegateLoader);
Class[] otherSideParameterClasses = method.getParameterTypes();
Class[] parameterClasses = new Class[otherSideParameterClasses.length];
for (int n = 0; n < parameterClasses.length; n++) {
Class<?> clazz = otherSideParameterClasses[n];
// Primitive classes are shared between the classloaders - so we can use the same
// primitive class declarations on either side. Non-primitive classes must be looked up
// by name.
parameterClasses[n] = clazz.isPrimitive()
? clazz
: Class.forName(clazz.getName(), true, delegateLoader);
}
return declaringClass.getDeclaredMethod(method.getName(), parameterClasses);
}
/**
* Returns an implementation of the boundary interface named clazz, by delegating method calls
* to the {@link InvocationHandler} invocationHandler.
*/
public static <T> T castToSuppLibClass(Class<T> clazz, InvocationHandler invocationHandler) {
return clazz.cast(
Proxy.newProxyInstance(BoundaryInterfaceReflectionUtil.class.getClassLoader(),
new Class[] {clazz}, invocationHandler));
}
/**
* Create an {@link java.lang.reflect.InvocationHandler} that delegates method calls to
* {@param delegate}, making sure that the {@link java.lang.reflect.Method} and parameters being
* passed to {@param delegate} exist in the same {@link java.lang.ClassLoader} as {@param
* delegate}.
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public static InvocationHandler createInvocationHandlerFor(final Object delegate) {
final ClassLoader delegateLoader = delegate.getClass().getClassLoader();
return new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
try {
return dupeMethod(method, delegateLoader).invoke(delegate, objects);
} catch (InvocationTargetException e) {
// If something went wrong, ensure we throw the original exception.
throw e.getTargetException();
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Reflection failed for method " + method, e);
}
}
};
}
}