1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 package org.chromium.support_lib_boundary.util; 5 6 import android.annotation.TargetApi; 7 import android.os.Build; 8 9 import java.lang.reflect.InvocationHandler; 10 import java.lang.reflect.InvocationTargetException; 11 import java.lang.reflect.Method; 12 import java.lang.reflect.Proxy; 13 14 /** 15 * A set of utility methods used for calling across the support library boundary. 16 */ 17 public class BoundaryInterfaceReflectionUtil { 18 /** 19 * Utility method for fetching a method from {@param delegateLoader}, with the same signature 20 * (package + class + method name + parameters) as a given method defined in another 21 * classloader. 22 */ 23 public static Method dupeMethod(Method method, ClassLoader delegateLoader) 24 throws ClassNotFoundException, NoSuchMethodException { 25 Class<?> declaringClass = 26 Class.forName(method.getDeclaringClass().getName(), true, delegateLoader); 27 Class[] otherSideParameterClasses = method.getParameterTypes(); 28 Class[] parameterClasses = new Class[otherSideParameterClasses.length]; 29 for (int n = 0; n < parameterClasses.length; n++) { 30 Class<?> clazz = otherSideParameterClasses[n]; 31 // Primitive classes are shared between the classloaders - so we can use the same 32 // primitive class declarations on either side. Non-primitive classes must be looked up 33 // by name. 34 parameterClasses[n] = clazz.isPrimitive() 35 ? clazz 36 : Class.forName(clazz.getName(), true, delegateLoader); 37 } 38 return declaringClass.getDeclaredMethod(method.getName(), parameterClasses); 39 } 40 41 /** 42 * Returns an implementation of the boundary interface named clazz, by delegating method calls 43 * to the {@link InvocationHandler} invocationHandler. 44 */ 45 public static <T> T castToSuppLibClass(Class<T> clazz, InvocationHandler invocationHandler) { 46 return clazz.cast( 47 Proxy.newProxyInstance(BoundaryInterfaceReflectionUtil.class.getClassLoader(), 48 new Class[] {clazz}, invocationHandler)); 49 } 50 51 /** 52 * Create an {@link java.lang.reflect.InvocationHandler} that delegates method calls to 53 * {@param delegate}, making sure that the {@link java.lang.reflect.Method} and parameters being 54 * passed to {@param delegate} exist in the same {@link java.lang.ClassLoader} as {@param 55 * delegate}. 56 */ 57 @TargetApi(Build.VERSION_CODES.KITKAT) 58 public static InvocationHandler createInvocationHandlerFor(final Object delegate) { 59 final ClassLoader delegateLoader = delegate.getClass().getClassLoader(); 60 return new InvocationHandler() { 61 @Override 62 public Object invoke(Object o, Method method, Object[] objects) throws Throwable { 63 try { 64 return dupeMethod(method, delegateLoader).invoke(delegate, objects); 65 } catch (InvocationTargetException e) { 66 // If something went wrong, ensure we throw the original exception. 67 throw e.getTargetException(); 68 } catch (ReflectiveOperationException e) { 69 throw new RuntimeException("Reflection failed for method " + method, e); 70 } 71 } 72 }; 73 } 74 } 75