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();
}
}
}
}