LoadedApkHuaWei

352 阅读4分钟
import android.app.ActivityManager;
import android.content.Context;
import android.text.TextUtils;

import com.zhangyue.iReader.app.APP;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;


/**
 * Fix crash only on HuaWei's device
 * <pre>
 * java.lang.AssertionError:<font color='red'>Register too many Broadcast Receivers</font>
 * android.app.LoadedApk.checkRecevierRegisteredLeakLocked(LoadedApk.java:1010)
 * android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:1038)
 * android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1476)
 * android.app.ContextImpl.registerReceiver(ContextImpl.java:1456)
 * android.app.ContextImpl.registerReceiver(ContextImpl.java:1450)
 * android.content.ContextWrapper.registerReceiver(ContextWrapper.java:586)
 * </pre>
 * 测试机型:华为p9(8.0.0) mata20(9) honor6(4.4.2)
 *
 * @author llew
 * @date 2018/1/14
 * https://github.com/llew2011/HuaWeiVerifier/blob/master/library/src/main/java/com/llew/huawei/verifier/LoadedApkHuaWei.java
 */
public final class LoadedApkHuaWei {

    private static final HuaWeiVerifier IMPL;

    static {
        final int version = android.os.Build.VERSION.SDK_INT;
        if (version >= 28) {
            IMPL = new V28VerifierImpl();
        } else if (version >= 26) {
            IMPL = new V26VerifierImpl();
        } else if (version >= 24) {
            IMPL = new V24VerifierImpl();
        } else {
            IMPL = new BaseVerifierImpl();
        }
    }

    public static void hookHuaWeiVerifier() {
        hookHuaWeiVerifier(null);
    }

    public static void hookHuaWeiVerifier(TooManyBroadcastCallback callback) {
        try {
            if (null != APP.getAppBaseContext()) {
                IMPL.verifier(APP.getAppBaseContext(), callback);
            } else {
                LOG.W(LoadedApkHuaWei.class.getSimpleName(), "application context is null !!!");
            }
        } catch (Throwable ignored) {
            // ignore it
        }
    }

    private static class V28VerifierImpl extends V26VerifierImpl {

        //9.0最多1000个广播,具体限制在哪未找到
        private static final int MAX_BROADCAST_COUNT = 1000;
        private static final String REGISTER_RECEIVER = "registerReceiver";
        private static final String UNREGISTER_RECEIVER = "unregisterReceiver";
        private static final String ACTIVITY_MANAGER = "android.app.IActivityManager";

        @Override
        public boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable {
            final boolean verified = super.verifier(baseContext, callback);
            if (verified) {
                hookActivityManagerService(baseContext.getClassLoader(), callback);
            }
            return verified;
        }

        private void hookActivityManagerService(ClassLoader loader, TooManyBroadcastCallback callback) {
            try {
                Object IActivityManagerSingletonObject = readStaticField(ActivityManager.class.getName(), "IActivityManagerSingleton");
                if (null != IActivityManagerSingletonObject) {
                    Object IActivityManagerObject = readField(IActivityManagerSingletonObject, "mInstance");
                    if (null != IActivityManagerObject) {
                        AmsInvocationHandler handler = new AmsInvocationHandler(IActivityManagerObject, callback);
                        Class<?> IActivityManagerClass = Class.forName(ACTIVITY_MANAGER);
                        Object proxy = Proxy.newProxyInstance(loader, new Class<?>[]{IActivityManagerClass}, handler);
                        writeField(IActivityManagerSingletonObject, "mInstance", proxy);
                    }
                }
            } catch (Throwable ignored) {
                // ignore it
            }
        }

        private static class AmsInvocationHandler implements InvocationHandler {

            private Object mIActivityManagerObject;

            private TooManyBroadcastCallback mCallback;

            private volatile int mCurrentBroadcastCount;

            private AmsInvocationHandler(Object mIActivityManagerObject, TooManyBroadcastCallback callback) {
                this.mCallback = callback;
                this.mIActivityManagerObject = mIActivityManagerObject;
            }

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                final String methodName = method.getName();
                if (TextUtils.equals(REGISTER_RECEIVER, methodName)) {
                    if (mCurrentBroadcastCount >= MAX_BROADCAST_COUNT) {
                        if (null != mCallback) {
                            mCallback.tooManyBroadcast(mCurrentBroadcastCount, MAX_BROADCAST_COUNT);
                        }
                        return null;
                    }
                    mCurrentBroadcastCount++;
                    if (null != mCallback) {
                        mCallback.tooManyBroadcast(mCurrentBroadcastCount, MAX_BROADCAST_COUNT);
                    }
                } else if (TextUtils.equals(UNREGISTER_RECEIVER, methodName)) {
                    mCurrentBroadcastCount--;
                    mCurrentBroadcastCount = mCurrentBroadcastCount < 0 ? 0 : mCurrentBroadcastCount;
                    if (null != mCallback) {
                        mCallback.tooManyBroadcast(mCurrentBroadcastCount, MAX_BROADCAST_COUNT);
                    }
                }
                try {
                    //最多1000个广播,具体修改位置未找到,先catch异常,可能存在广告接收不到的情况
                    return method.invoke(mIActivityManagerObject, args);
                } catch (Exception e) {
                    return null;
                }
            }
        }
    }

    private static class V26VerifierImpl extends BaseVerifierImpl {

        private static final String WHITE_LIST = "mWhiteListMap";

        @Override
        public boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable {
            Object whiteListMapObject = getWhiteListObject(baseContext, WHITE_LIST);
            if (whiteListMapObject instanceof Map) {
                Map whiteListMap = (Map) whiteListMapObject;
                List whiteList = (List) whiteListMap.get(0);
                if (null == whiteList) {
                    whiteList = new ArrayList<>();
                    whiteListMap.put(0, whiteList);
                }
                whiteList.add(baseContext.getPackageName());
                return true;
            }
            return false;
        }
    }

    private static class V24VerifierImpl extends BaseVerifierImpl {

        private static final String WHITE_LIST = "mWhiteList";

        @Override
        public boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable {
            Object whiteListObject = getWhiteListObject(baseContext, WHITE_LIST);
            if (whiteListObject instanceof List) {
                List whiteList = (List) whiteListObject;
                whiteList.add(baseContext.getPackageName());
                return true;
            }
            return false;
        }
    }

    private static class BaseVerifierImpl implements HuaWeiVerifier {

        private static final String WHITE_LIST = "mWhiteList";

        @Override
        public boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable {
            Object receiverResourceObject = getReceiverResourceObject(baseContext);
            Object whiteListObject = getWhiteListObject(receiverResourceObject, WHITE_LIST);
            if (whiteListObject instanceof String[]) {
                String[] whiteList = (String[]) whiteListObject;
                List<String> newWhiteList = new ArrayList<>();
                newWhiteList.add(baseContext.getPackageName());
                Collections.addAll(newWhiteList, whiteList);
                writeField(receiverResourceObject, WHITE_LIST, newWhiteList.toArray(new String[newWhiteList.size()]));
                return true;
            } else {
                if (null != receiverResourceObject) {
                    writeField(receiverResourceObject, "mResourceConfig", null);
                }
            }
            return false;
        }

        Object getWhiteListObject(Context baseContext, String whiteList) {
            return getWhiteListObject(getReceiverResourceObject(baseContext), whiteList);
        }

        private Object getWhiteListObject(Object receiverResourceObject, String whiteList) {
            try {
                if (null != receiverResourceObject) {
                    return readField(receiverResourceObject, whiteList);
                }
            } catch (Throwable ignored) {
            }
            return null;
        }

        private Object getReceiverResourceObject(Context baseContext) {
            try {
                Field receiverResourceField = getDeclaredField("android.app.LoadedApk", "mReceiverResource", true);
                if (null != receiverResourceField) {
                    Field packageInfoField = getDeclaredField("android.app.ContextImpl", "mPackageInfo", true);
                    if (null != packageInfoField) {
                        Object packageInfoObject = readField(packageInfoField, baseContext);
                        if (null != packageInfoObject) {
                            return readField(receiverResourceField, packageInfoObject, true);
                        }
                    }
                }
            } catch (Throwable ignored) {
            }
            return null;
        }
    }

    private interface HuaWeiVerifier {
        boolean verifier(Context baseContext, TooManyBroadcastCallback callback) throws Throwable;
    }

    public interface TooManyBroadcastCallback {
        void tooManyBroadcast(int registedCount, int totalCount);
    }


    //以下均为反射帮助方法===============================================


    private static final Map<String, Field> sFieldCache = new HashMap<String, Field>();
    private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;

    //field相关方法 ===================================================

    private static String getKey(Class<?> cls, String fieldName) {
        StringBuilder sb = new StringBuilder();
        sb.append(cls.toString()).append("#").append(fieldName);
        return sb.toString();
    }

    private static Field getField(Class<?> cls, String fieldName, final boolean forceAccess) {
        isTrue(cls != null, "The class must not be null");
        isTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty");

        String key = getKey(cls, fieldName);
        Field cachedField;
        synchronized (sFieldCache) {
            cachedField = sFieldCache.get(key);
        }
        if (cachedField != null) {
            if (forceAccess && !cachedField.isAccessible()) {
                cachedField.setAccessible(true);
            }
            return cachedField;
        }

        // check up the superclass hierarchy
        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
            try {
                final Field field = acls.getDeclaredField(fieldName);
                // getDeclaredField checks for non-public scopes as well
                // and it returns accurate results
                if (!Modifier.isPublic(field.getModifiers())) {
                    if (forceAccess) {
                        field.setAccessible(true);
                    } else {
                        continue;
                    }
                }
                synchronized (sFieldCache) {
                    sFieldCache.put(key, field);
                }
                return field;
            } catch (final NoSuchFieldException ex) { // NOPMD
                // ignore
            }
        }
        // check the public interface case. This must be manually searched for
        // incase there is a public supersuperclass field hidden by a private/package
        // superclass field.
        Field match = null;
        for (final Class<?> class1 : getAllInterfaces(cls)) {
            try {
                final Field test = class1.getField(fieldName);
                isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
                        + "; a matching field exists on two or more implemented interfaces.", fieldName, cls);
                match = test;
            } catch (final NoSuchFieldException ex) { // NOPMD
                // ignore
            }
        }
        synchronized (sFieldCache) {
            sFieldCache.put(key, match);
        }
        return match;
    }

    private static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException {
        isTrue(field != null, "The field must not be null");
        if (forceAccess && !field.isAccessible()) {
            field.setAccessible(true);
        } else {
            setAccessibleWorkaround(field);
        }
        return field.get(target);
    }


    private static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess)
            throws IllegalAccessException {
        isTrue(field != null, "The field must not be null");
        if (forceAccess && !field.isAccessible()) {
            field.setAccessible(true);
        } else {
            setAccessibleWorkaround(field);
        }
        field.set(target, value);
    }


    private static Object readField(final Field field, final Object target) throws IllegalAccessException {
        return readField(field, target, true);
    }

    private static Object readField(final Object target, final String fieldName) throws IllegalAccessException {
        isTrue(target != null, "target object must not be null");
        final Class<?> cls = target.getClass();
        final Field field = getField(cls, fieldName, true);
        isTrue(field != null, "Cannot locate field %s on %s", fieldName, cls);
        // already forced access above, don't repeat it here:
        return readField(field, target, false);
    }


    private static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException {
        writeField(target, fieldName, value, true);
    }

    private static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess) throws IllegalAccessException {
        isTrue(target != null, "target object must not be null");
        final Class<?> cls = target.getClass();
        final Field field = getField(cls, fieldName, true);
        isTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName);
        // already forced access above, don't repeat it here:
        writeField(field, target, value, forceAccess);
    }

    private static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException {
        isTrue(field != null, "The field must not be null");
        isTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static", field.getName());
        return readField(field, (Object) null, forceAccess);
    }

    private static Object readStaticField(final String cls, final String fieldName) throws IllegalAccessException {
        try {
            return readStaticField(Class.forName(cls), fieldName);
        } catch (Throwable ignored) {
            print(ignored);
        }
        return null;
    }

    private static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException {
        final Field field = getField(cls, fieldName, true);
        isTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls);
        // already forced access above, don't repeat it here:
        return readStaticField(field, true);
    }

    private static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException {
        isTrue(field != null, "The field must not be null");
        isTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static", field.getDeclaringClass().getName(),
                field.getName());
        writeField(field, (Object) null, value, forceAccess);
    }

    private static Field getDeclaredField(final String cls, final String fieldName, final boolean forceAccess) {
        try {
            return getDeclaredField(Class.forName(cls), fieldName, forceAccess);
        } catch (Throwable ignore) {
            print(ignore);
        }
        return null;
    }

    private static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) {
        isTrue(cls != null, "The class must not be null");
        isTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty");
        try {
            // only consider the specified class by using getDeclaredField()
            final Field field = cls.getDeclaredField(fieldName);
            if (!isAccessible(field)) {
                if (forceAccess) {
                    field.setAccessible(true);
                } else {
                    return null;
                }
            }
            return field;
        } catch (final NoSuchFieldException e) { // NOPMD
            // ignore
        }
        return null;
    }

    //field相关方法 结束 ===================================================
    //Member相关方法 ======================================================

    private static boolean isPackageAccess(final int modifiers) {
        return (modifiers & ACCESS_TEST) == 0;
    }

    static boolean isAccessible(final Member m) {
        return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic();
    }

    static boolean setAccessibleWorkaround(final AccessibleObject o) {
        if (o == null || o.isAccessible()) {
            return false;
        }
        final Member m = (Member) o;
        if (!o.isAccessible() && Modifier.isPublic(m.getModifiers()) && isPackageAccess(m.getDeclaringClass().getModifiers())) {
            try {
                o.setAccessible(true);
                return true;
            } catch (final SecurityException e) { // NOPMD
                // ignore in favor of subsequent IllegalAccessException
            }
        }
        return false;
    }
    //Member相关方法 结束 ==================================================

    public static List<Class<?>> getAllInterfaces(final Class<?> cls) {
        if (cls == null) {
            return null;
        }
        final LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<Class<?>>();
        getAllInterfaces(cls, interfacesFound);
        return new ArrayList<Class<?>>(interfacesFound);
    }

    private static void getAllInterfaces(Class<?> cls, final HashSet<Class<?>> interfacesFound) {
        while (cls != null) {
            final Class<?>[] interfaces = cls.getInterfaces();

            for (final Class<?> i : interfaces) {
                if (interfacesFound.add(i)) {
                    getAllInterfaces(i, interfacesFound);
                }
            }

            cls = cls.getSuperclass();
        }
    }

    public static void print(Throwable throwable) {
        if (null != throwable) {
            throwable.printStackTrace();
        }
    }

    private static void isTrue(final boolean expression, final String message, final Object... values) {
        if (!expression) {
            throw new IllegalArgumentException(String.format(message, values));
        }
    }
}