package com.llew.huawei.verifier; import android.app.ActivityManager; import android.app.Application; import android.content.Context; import android.text.TextUtils; import android.util.Log; import com.llew.reflect.FieldUtils; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /** * Fix crash only on HuaWei's device * <pre class="prettyprint"> * 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> * * @author llew * @date 2018/1/14 */ 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(Application application) { hookHuaWeiVerifier(application, null); } public static void hookHuaWeiVerifier(Application application, TooManyBroadcastCallback callback) { try { if (null != application) { IMPL.verifier(application.getBaseContext(), callback); } else { Log.w(LoadedApkHuaWei.class.getSimpleName(), "application is null !!!"); } } catch (Throwable ignored) { // ignore it } } private static class V28VerifierImpl extends V26VerifierImpl { 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); Log.v(LoadedApkHuaWei.class.getSimpleName(), "verified: " + verified); hookActivityManagerService(baseContext.getClassLoader(), callback); return verified; } private void hookActivityManagerService(ClassLoader loader, TooManyBroadcastCallback callback) { try { Object IActivityManagerSingletonObject = FieldUtils.readStaticField(ActivityManager.class.getName(), "IActivityManagerSingleton"); if (null != IActivityManagerSingletonObject) { Object IActivityManagerObject = FieldUtils.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); FieldUtils.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); } } return method.invoke(mIActivityManagerObject, args); } } } 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); FieldUtils.writeField(receiverResourceObject, WHITE_LIST, newWhiteList.toArray(new String[newWhiteList.size()])); return true; } else { if (null != receiverResourceObject) { FieldUtils.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 FieldUtils.readField(receiverResourceObject, whiteList); } } catch (Throwable ignored) { } return null; } private Object getReceiverResourceObject(Context baseContext) { try { Field receiverResourceField = FieldUtils.getDeclaredField("android.app.LoadedApk", "mReceiverResource", true); if (null != receiverResourceField) { Field packageInfoField = FieldUtils.getDeclaredField("android.app.ContextImpl", "mPackageInfo", true); if (null != packageInfoField) { Object packageInfoObject = FieldUtils.readField(packageInfoField, baseContext); if (null != packageInfoObject) { return FieldUtils.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); } }