package com.lody.virtual.server.am; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Message; import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.client.env.SpecialComponentList; import com.lody.virtual.helper.collection.ArrayMap; import com.lody.virtual.helper.utils.VLog; import com.lody.virtual.remote.PendingResultData; import com.lody.virtual.server.pm.PackageSetting; import com.lody.virtual.server.pm.VAppManagerService; import com.lody.virtual.server.pm.parser.VPackage; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import mirror.android.app.ContextImpl; import mirror.android.app.LoadedApkHuaWei; import mirror.android.rms.resource.ReceiverResourceLP; import mirror.android.rms.resource.ReceiverResourceM; import mirror.android.rms.resource.ReceiverResourceN; import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; /** * @author Lody */ public class BroadcastSystem { private static final String TAG = BroadcastSystem.class.getSimpleName(); /** * MUST < 10000. */ private static final int BROADCAST_TIME_OUT = 8500; private static BroadcastSystem gDefault; private final ArrayMap<String, List<BroadcastReceiver>> mReceivers = new ArrayMap<>(); private final Map<IBinder, BroadcastRecord> mBroadcastRecords = new HashMap<>(); private final Context mContext; private final StaticScheduler mScheduler; private final TimeoutHandler mTimeoutHandler; private final VActivityManagerService mAMS; private final VAppManagerService mApp; private BroadcastSystem(Context context, VActivityManagerService ams, VAppManagerService app) { this.mContext = context; this.mApp = app; this.mAMS = ams; mScheduler = new StaticScheduler(); mTimeoutHandler = new TimeoutHandler(); fuckHuaWeiVerifier(); } public static void attach(VActivityManagerService ams, VAppManagerService app) { if (gDefault != null) { throw new IllegalStateException(); } gDefault = new BroadcastSystem(VirtualCore.get().getContext(), ams, app); } public static BroadcastSystem get() { return gDefault; } /** * FIX ISSUE #171: * java.lang.AssertionError: Register too many Broadcast Receivers * at android.app.LoadedApk.checkRecevierRegisteredLeakLocked(LoadedApk.java:772) * at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:800) * at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1329) * at android.app.ContextImpl.registerReceiver(ContextImpl.java:1309) * at com.lody.virtual.server.am.BroadcastSystem.startApp(BroadcastSystem.java:54) * at com.lody.virtual.server.pm.VAppManagerService.install(VAppManagerService.java:193) * at com.lody.virtual.server.pm.VAppManagerService.preloadAllApps(VAppManagerService.java:98) * at com.lody.virtual.server.pm.VAppManagerService.systemReady(VAppManagerService.java:70) * at com.lody.virtual.server.BinderProvider.onCreate(BinderProvider.java:42) */ private void fuckHuaWeiVerifier() { if (LoadedApkHuaWei.mReceiverResource != null) { Object packageInfo = ContextImpl.mPackageInfo.get(mContext); if (packageInfo != null) { Object receiverResource = LoadedApkHuaWei.mReceiverResource.get(packageInfo); if (receiverResource != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (ReceiverResourceN.mWhiteList != null) { List<String> whiteList = ReceiverResourceN.mWhiteList.get(receiverResource); List<String> newWhiteList = new ArrayList<>(); // Add our package name to the white list. newWhiteList.add(mContext.getPackageName()); if (whiteList != null) { newWhiteList.addAll(whiteList); } ReceiverResourceN.mWhiteList.set(receiverResource, newWhiteList); } } else { if (ReceiverResourceM.mWhiteList != null) { String[] whiteList = ReceiverResourceM.mWhiteList.get(receiverResource); List<String> newWhiteList = new LinkedList<>(); Collections.addAll(newWhiteList, whiteList); // Add our package name to the white list. newWhiteList.add(mContext.getPackageName()); ReceiverResourceM.mWhiteList.set(receiverResource, newWhiteList.toArray(new String[newWhiteList.size()])); } else if (ReceiverResourceLP.mResourceConfig != null) { // Just clear the ResourceConfig. ReceiverResourceLP.mResourceConfig.set(receiverResource, null); } } } } } } public void startApp(VPackage p) { PackageSetting setting = (PackageSetting) p.mExtras; for (VPackage.ActivityComponent receiver : p.receivers) { ActivityInfo info = receiver.info; List<BroadcastReceiver> receivers = mReceivers.get(p.packageName); if (receivers == null) { receivers = new ArrayList<>(); mReceivers.put(p.packageName, receivers); } String componentAction = String.format("_VA_%s_%s", info.packageName, info.name); IntentFilter componentFilter = new IntentFilter(componentAction); BroadcastReceiver r = new StaticBroadcastReceiver(setting.appId, info, componentFilter); mContext.registerReceiver(r, componentFilter, null, mScheduler); receivers.add(r); for (VPackage.ActivityIntentInfo ci : receiver.intents) { IntentFilter cloneFilter = new IntentFilter(ci.filter); SpecialComponentList.protectIntentFilter(cloneFilter); r = new StaticBroadcastReceiver(setting.appId, info, cloneFilter); mContext.registerReceiver(r, cloneFilter, null, mScheduler); receivers.add(r); } } } public void stopApp(String packageName) { synchronized (mBroadcastRecords) { Iterator<Map.Entry<IBinder, BroadcastRecord>> iterator = mBroadcastRecords.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<IBinder, BroadcastRecord> entry = iterator.next(); BroadcastRecord record = entry.getValue(); if (record.receiverInfo.packageName.equals(packageName)) { record.pendingResult.finish(); iterator.remove(); } } } synchronized (mReceivers) { List<BroadcastReceiver> receivers = mReceivers.get(packageName); if (receivers != null) { for (BroadcastReceiver r : receivers) { mContext.unregisterReceiver(r); } } mReceivers.remove(packageName); } } void broadcastFinish(PendingResultData res) { synchronized (mBroadcastRecords) { BroadcastRecord record = mBroadcastRecords.remove(res.mToken); if (record == null) { VLog.e(TAG, "Unable to find the BroadcastRecord by token: " + res.mToken); } } mTimeoutHandler.removeMessages(0, res.mToken); res.finish(); } void broadcastSent(int vuid, ActivityInfo receiverInfo, PendingResultData res) { BroadcastRecord record = new BroadcastRecord(vuid, receiverInfo, res); synchronized (mBroadcastRecords) { mBroadcastRecords.put(res.mToken, record); } Message msg = new Message(); msg.obj = res.mToken; mTimeoutHandler.sendMessageDelayed(msg, BROADCAST_TIME_OUT); } private static final class StaticScheduler extends Handler { } private static final class BroadcastRecord { int vuid; ActivityInfo receiverInfo; PendingResultData pendingResult; BroadcastRecord(int vuid, ActivityInfo receiverInfo, PendingResultData pendingResult) { this.vuid = vuid; this.receiverInfo = receiverInfo; this.pendingResult = pendingResult; } } private final class TimeoutHandler extends Handler { @Override public void handleMessage(Message msg) { IBinder token = (IBinder) msg.obj; BroadcastRecord r = mBroadcastRecords.remove(token); if (r != null) { VLog.w(TAG, "Broadcast timeout, cancel to dispatch it."); r.pendingResult.finish(); } } } private final class StaticBroadcastReceiver extends BroadcastReceiver { private int appId; private ActivityInfo info; @SuppressWarnings("unused") private IntentFilter filter; private StaticBroadcastReceiver(int appId, ActivityInfo info, IntentFilter filter) { this.appId = appId; this.info = info; this.filter = filter; } @Override public void onReceive(Context context, Intent intent) { if (mApp.isBooting()) { return; } if ((intent.getFlags() & FLAG_RECEIVER_REGISTERED_ONLY) != 0 || isInitialStickyBroadcast()) { return; } PendingResult result = goAsync(); if (!mAMS.handleStaticBroadcast(appId, info, intent, new PendingResultData(result))) { result.finish(); } } } }