/* * Tencent is pleased to support the open source community by making Tinker available. * * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause * * 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.tencent.tinker.loader.shareutil; import android.content.Context; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; /** * Created by zhangshaowen on 16/8/22. */ public class ShareReflectUtil { /** * Locates a given field anywhere in the class inheritance hierarchy. * * @param instance an object to search the field into. * @param name field name * @return a field object * @throws NoSuchFieldException if the field cannot be located */ public static Field findField(Object instance, String name) throws NoSuchFieldException { for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) { try { Field field = clazz.getDeclaredField(name); if (!field.isAccessible()) { field.setAccessible(true); } return field; } catch (NoSuchFieldException e) { // ignore and search next } } throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass()); } public static Field findField(Class<?> originClazz, String name) throws NoSuchFieldException { for (Class<?> clazz = originClazz; clazz != null; clazz = clazz.getSuperclass()) { try { Field field = clazz.getDeclaredField(name); if (!field.isAccessible()) { field.setAccessible(true); } return field; } catch (NoSuchFieldException e) { // ignore and search next } } throw new NoSuchFieldException("Field " + name + " not found in " + originClazz); } /** * Locates a given method anywhere in the class inheritance hierarchy. * * @param instance an object to search the method into. * @param name method name * @param parameterTypes method parameter types * @return a method object * @throws NoSuchMethodException if the method cannot be located */ public static Method findMethod(Object instance, String name, Class<?>... parameterTypes) throws NoSuchMethodException { for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) { try { Method method = clazz.getDeclaredMethod(name, parameterTypes); if (!method.isAccessible()) { method.setAccessible(true); } return method; } catch (NoSuchMethodException e) { // ignore and search next } } throw new NoSuchMethodException("Method " + name + " with parameters " + Arrays.asList(parameterTypes) + " not found in " + instance.getClass()); } /** * Replace the value of a field containing a non null array, by a new array containing the * elements of the original array plus the elements of extraElements. * * @param instance the instance whose field is to be modified. * @param fieldName the field to modify. * @param extraElements elements to append at the end of the array. */ public static void expandFieldArray(Object instance, String fieldName, Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field jlrField = findField(instance, fieldName); Object[] original = (Object[]) jlrField.get(instance); Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), original.length + extraElements.length); // NOTE: changed to copy extraElements first, for patch load first System.arraycopy(extraElements, 0, combined, 0, extraElements.length); System.arraycopy(original, 0, combined, extraElements.length, original.length); jlrField.set(instance, combined); } /** * Replace the value of a field containing a non null array, by a new array containing the * elements of the original array plus the elements of extraElements. * * @param instance the instance whose field is to be modified. * @param fieldName the field to modify. */ public static void reduceFieldArray(Object instance, String fieldName, int reduceSize) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { if (reduceSize <= 0) { return; } Field jlrField = findField(instance, fieldName); Object[] original = (Object[]) jlrField.get(instance); int finalLength = original.length - reduceSize; if (finalLength <= 0) { return; } Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), finalLength); System.arraycopy(original, reduceSize, combined, 0, finalLength); jlrField.set(instance, combined); } public static Object getActivityThread(Context context, Class<?> activityThread) { try { if (activityThread == null) { activityThread = Class.forName("android.app.ActivityThread"); } Method m = activityThread.getMethod("currentActivityThread"); m.setAccessible(true); Object currentActivityThread = m.invoke(null); if (currentActivityThread == null && context != null) { // In older versions of Android (prior to frameworks/base 66a017b63461a22842) // the currentActivityThread was built on thread locals, so we'll need to try // even harder Field mLoadedApk = context.getClass().getField("mLoadedApk"); mLoadedApk.setAccessible(true); Object apk = mLoadedApk.get(context); Field mActivityThreadField = apk.getClass().getDeclaredField("mActivityThread"); mActivityThreadField.setAccessible(true); currentActivityThread = mActivityThreadField.get(apk); } return currentActivityThread; } catch (Throwable ignore) { return null; } } }