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.content.pm.PackageParser; 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.proto.AppSetting; import com.lody.virtual.helper.proto.PendingResultData; import com.lody.virtual.helper.utils.VLog; import com.lody.virtual.helper.utils.collection.ArrayMap; import com.lody.virtual.server.pm.VAppManagerService; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; 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(); private static final Set<String> SYSTEM_BROADCAST_ACTION = new HashSet<>(7); private static final Set<String> SYSTEM_STICKY_BROADCAST_ACTION = new HashSet<>(4); /** * MUST < 10000. */ private static final int BROADCAST_TIME_OUT = 8500; private static BroadcastSystem gDefault; static { SYSTEM_BROADCAST_ACTION.add("android.net.wifi.STATE_CHANGE"); SYSTEM_BROADCAST_ACTION.add("android.net.wifi.WIFI_STATE_CHANGED"); SYSTEM_BROADCAST_ACTION.add("android.net.conn.CONNECTIVITY_CHANGE"); SYSTEM_BROADCAST_ACTION.add("android.intent.action.BATTERY_CHANGED"); SYSTEM_BROADCAST_ACTION.add("android.intent.action.BATTERY_LOW"); SYSTEM_BROADCAST_ACTION.add("android.intent.action.BATTERY_OKAY"); SYSTEM_BROADCAST_ACTION.add("android.intent.action.ANY_DATA_STATE"); SYSTEM_STICKY_BROADCAST_ACTION.add("android.net.conn.CONNECTIVITY_CHANGE"); SYSTEM_STICKY_BROADCAST_ACTION.add("android.net.wifi.WIFI_STATE_CHANGED"); SYSTEM_STICKY_BROADCAST_ACTION.add("android.intent.action.BATTERY_CHANGED"); SYSTEM_STICKY_BROADCAST_ACTION.add("android.intent.action.ANY_DATA_STATE"); } private final ArrayMap<String, SystemBroadcastReceiver> mSystemReceivers = new ArrayMap<>(); 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(); registerSystemReceiver(); } 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; } Intent dispatchStickyBroadcast(int vuid, IntentFilter filter) { Iterator<String> iterator = filter.actionsIterator(); while (iterator.hasNext()) { String action = iterator.next(); SystemBroadcastReceiver receiver = mSystemReceivers.get(action); if (receiver != null && receiver.sticky && receiver.stickyIntent != null) { Intent intent = new Intent(receiver.stickyIntent); SpecialComponentList.protectIntent(intent); intent.putExtra("_VA_|_uid_", vuid); mContext.sendBroadcast(intent); if (!iterator.hasNext()) { return receiver.stickyIntent; } } } return null; } private void registerSystemReceiver() { for (String action : SYSTEM_BROADCAST_ACTION) { SystemBroadcastReceiver receiver = new SystemBroadcastReceiver(false); mContext.registerReceiver(receiver, new IntentFilter(action)); mSystemReceivers.put(action, receiver); } for (String action : SYSTEM_STICKY_BROADCAST_ACTION) { SystemBroadcastReceiver receiver = mSystemReceivers.get(action); if (receiver != null) { receiver.sticky = true; } } } /** * 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(PackageParser.Package p) { AppSetting setting = (AppSetting) p.mExtras; for (PackageParser.Activity receiver : p.receivers) { ActivityInfo info = receiver.info; List<? extends IntentFilter> filters = receiver.intents; 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 (IntentFilter filter : filters) { IntentFilter cloneFilter = new IntentFilter(filter); redirectFilterActions(cloneFilter); r = new StaticBroadcastReceiver(setting.appId, info, cloneFilter); mContext.registerReceiver(r, cloneFilter, null, mScheduler); receivers.add(r); } } } private void redirectFilterActions(IntentFilter filter) { List<String> actions = mirror.android.content.IntentFilter.mActions.get(filter); ListIterator<String> iterator = actions.listIterator(); while (iterator.hasNext()) { String action = iterator.next(); if (SpecialComponentList.isActionInBlackList(action)) { iterator.remove(); continue; } String protectedAction = SpecialComponentList.protectAction(action); if (protectedAction != null) { iterator.set(protectedAction); } } } 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 SystemBroadcastReceiver extends BroadcastReceiver { boolean sticky; Intent stickyIntent; public SystemBroadcastReceiver(boolean sticky) { this.sticky = sticky; } @Override public void onReceive(Context context, Intent intent) { Intent protectedIntent = new Intent(intent); SpecialComponentList.protectIntent(protectedIntent); mContext.sendBroadcast(protectedIntent); if (sticky) { stickyIntent = intent; } } } 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(); synchronized (mAMS) { if (!mAMS.handleStaticBroadcast(appId, info, intent, new PendingResultData(result))) { result.finish(); } } } } }