/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.fd.runtime;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.tools.fd.common.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.android.tools.fd.common.Log.logging;
/**
* Generic Instant Run services. must not depend on Android APIs.
*
* TODO: transform this static methods into interface/implementation.
*/
@SuppressWarnings("unused")
public class AndroidInstantRuntime {
protected interface Logging {
void log(@NonNull Level level, @NonNull String string);
boolean isLoggable(@NonNull Level level);
void log(@NonNull Level level, @NonNull String string, @Nullable Throwable throwable);
}
public static void setLogger(final Logger logger) {
logging = new Log.Logging() {
@Override
public void log(@NonNull Level level, @NonNull String string) {
logger.log(level, string);
}
@Override
public boolean isLoggable(@NonNull Level level) {
return logger.isLoggable(level);
}
@Override
public void log(@NonNull Level level, @NonNull String string,
@Nullable Throwable throwable) {
logger.log(level, string, throwable);
}
};
}
/**
* 反射获取 Field 的值
*
* @param targetClass 目标类 Class
* @param fieldName Field name
* @return 目标 Field 的值
*/
@Nullable
public static Object getStaticPrivateField(Class targetClass, String fieldName) {
return getPrivateField(null /* targetObject */, targetClass, fieldName);
}
/**
* Hook 私有 Field 的值
*
* @param value 要设置的 Field 值
* @param targetClass 目标类 Class
* @param fieldName Field name
*/
public static void setStaticPrivateField(
@NonNull Object value, @NonNull Class targetClass, @NonNull String fieldName) {
setPrivateField(null /* targetObject */, value, targetClass, fieldName);
}
/**
* Hook 私有 Field 的值
*
* @param targetObject 目标类 实例
* @param value 要设置的 Field 值
* @param targetClass 目标类 Class
* @param fieldName Field name
*/
public static void setPrivateField(
@Nullable Object targetObject,
@Nullable Object value,
@NonNull Class targetClass,
@NonNull String fieldName) {
try {
Field declaredField = getField(targetClass, fieldName);
declaredField.set(targetObject, value);
} catch (IllegalAccessException e) {
if (logging != null) {
logging.log(Level.SEVERE,
String.format("Exception during setPrivateField %s", fieldName), e);
}
throw new RuntimeException(e);
}
}
/**
* 反射获取 私有 Field 的值
*
* @param targetObject 目标类 实例
* @param targetClass 目标类 Class
* @param fieldName Field name
* @return 私有 Field 的值
*/
@Nullable
public static Object getPrivateField(
@Nullable Object targetObject,
@NonNull Class targetClass,
@NonNull String fieldName) {
try {
Field declaredField = getField(targetClass, fieldName);
return declaredField.get(targetObject);
} catch (IllegalAccessException e) {
if (logging != null) {
logging.log(Level.SEVERE,
String.format("Exception during%1$s getField %2$s",
targetObject == null ? " static" : "",
fieldName), e);
}
throw new RuntimeException(e);
}
}
/**
* 反射获取 Field,并且设置为 public
*
* @param target 目标类 Class
* @param name Field name
* @return Field
*/
@NonNull
private static Field getField(Class target, String name) {
Field declareField = getFieldByName(target, name);
if (declareField == null) {
throw new RuntimeException(new NoSuchElementException(name));
}
declareField.setAccessible(true);
return declareField;
}
/**
* 反射调用 目标类 的方法
*
* @param receiver 目标类 实例
* @param params 参数值 Object[]
* @param parameterTypes 方法参数类型 Class[]
* @param methodName 方法名
* @return 方法返回值
* @throws Throwable
*/
public static Object invokeProtectedMethod(Object receiver,
Object[] params,
Class[] parameterTypes,
String methodName) throws Throwable {
if (logging != null && logging.isLoggable(Level.FINE)) {
logging.log(Level.FINE,
String.format("protectedMethod:%s on %s", methodName, receiver));
}
try {
Method toDispatchTo = getMethodByName(receiver.getClass(), methodName, parameterTypes);
if (toDispatchTo == null) {
throw new RuntimeException(new NoSuchMethodException(methodName));
}
toDispatchTo.setAccessible(true);
return toDispatchTo.invoke(receiver, params);
} catch (InvocationTargetException e) {
// The called method threw an exception, rethrow
throw e.getCause();
} catch (IllegalAccessException e) {
logging.log(Level.SEVERE, String.format("Exception while invoking %s", methodName), e);
throw new RuntimeException(e);
}
}
/**
* 反射调用 目标类 的方法
*
* @param params 参数值 Object[]
* @param parameterTypes 方法参数类型 Class[]
* @param methodName 方法名
* @param receiverClass 目标类 Class
* @return 方法返回值
* @throws Throwable
*/
public static Object invokeProtectedStaticMethod(
Object[] params,
Class[] parameterTypes,
String methodName,
Class receiverClass) throws Throwable {
if (logging != null && logging.isLoggable(Level.FINE)) {
logging.log(Level.FINE,
String.format("protectedStaticMethod:%s on %s", methodName,
receiverClass.getName()));
}
try {
Method toDispatchTo = getMethodByName(receiverClass, methodName, parameterTypes);
if (toDispatchTo == null) {
throw new RuntimeException(new NoSuchMethodException(
methodName + " in class " + receiverClass.getName()));
}
toDispatchTo.setAccessible(true);
return toDispatchTo.invoke(null /* target */, params);
} catch (InvocationTargetException e) {
// The called method threw an exception, rethrow
throw e.getCause();
} catch (IllegalAccessException e) {
logging.log(Level.SEVERE, String.format("Exception while invoking %s", methodName), e);
throw new RuntimeException(e);
}
}
/**
* 反射实例化目标类
*
* @param params 构造方法参数值 Object[]
* @param paramTypes 构造方法参数类型 Class[]
* @param targetClass 目标类 Class
* @param <T> Class 类型
* @return 目标类实例
* @throws Throwable
*/
public static <T> T newForClass(Object[] params, Class[] paramTypes, Class<T> targetClass)
throws Throwable {
Constructor declaredConstructor;
try {
declaredConstructor = targetClass.getDeclaredConstructor(paramTypes);
} catch (NoSuchMethodException e) {
logging.log(Level.SEVERE, "Exception while resolving constructor", e);
throw new RuntimeException(e);
}
declaredConstructor.setAccessible(true);
try {
return targetClass.cast(declaredConstructor.newInstance(params));
} catch (InvocationTargetException e) {
// The called method threw an exception, rethrow
throw e.getCause();
} catch (InstantiationException e) {
logging.log(Level.SEVERE,
String.format("Exception while instantiating %s", targetClass), e);
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
logging.log(Level.SEVERE,
String.format("Exception while instantiating %s", targetClass), e);
throw new RuntimeException(e);
}
}
/**
* 反射获取 Field
*
* @param aClass 目标类 Class
* @param name Field name
* @return field
*/
private static Field getFieldByName(Class<?> aClass, String name) {
if (logging != null && logging.isLoggable(Level.FINE)) {
logging.log(Level.FINE,
String.format("getFieldByName:%s in %s", name, aClass.getName()));
}
Class<?> currentClass = aClass;
while (currentClass != null) {
try {
return currentClass.getDeclaredField(name);
} catch (NoSuchFieldException e) {
// ignored.
}
currentClass = currentClass.getSuperclass();
}
return null;
}
/**
* 反射获取 Method
*
* @param aClass 目标类 Class
* @param name Method name
* @param paramTypes 方法参数
* @return Method
*/
private static Method getMethodByName(Class<?> aClass, String name, Class[] paramTypes) {
if (aClass == null) {
return null;
}
Class<?> currentClass = aClass;
while (currentClass != null) {
try {
return currentClass.getDeclaredMethod(name, paramTypes);
} catch (NoSuchMethodException e) {
// ignored.
}
currentClass = currentClass.getSuperclass();
if (currentClass != null && logging != null && logging.isLoggable(Level.FINE)) {
logging.log(Level.FINE, String.format(
"getMethodByName:Looking in %s now", currentClass.getName()));
}
}
return null;
}
public static void trace(String s) {
if (logging != null) {
logging.log(Level.FINE, s);
}
}
public static void trace(String s1, String s2) {
if (logging != null) {
logging.log(Level.FINE, String.format("%s %s", s1, s2));
}
}
public static void trace(String s1, String s2, String s3) {
if (logging != null) {
logging.log(Level.FINE, String.format("%s %s %s", s1, s2, s3));
}
}
public static void trace(String s1, String s2, String s3, String s4) {
if (logging != null) {
logging.log(Level.FINE, String.format("%s %s %s %s", s1, s2, s3, s4));
}
}
}