content: package com.lody.virtual.client.hook.proxies.am;
  
  import android.annotation.TargetApi;
  import android.app.ActivityManager;
  import android.app.Application;
  import android.app.IServiceConnection;
  import android.app.Notification;
  import android.app.Service;
  import android.content.ComponentName;
  import android.content.IIntentReceiver;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.content.pm.ActivityInfo;
  import android.content.pm.ComponentInfo;
  import android.content.pm.PackageManager;
  import android.content.pm.ProviderInfo;
  import android.content.pm.ResolveInfo;
  import android.content.pm.ServiceInfo;
  import android.content.res.Resources;
  import android.content.res.TypedArray;
  import android.graphics.Bitmap;
  import android.graphics.drawable.Drawable;
  import android.graphics.drawable.Icon;
  import android.net.Uri;
  import android.os.Build;
  import android.os.Bundle;
  import android.os.IBinder;
  import android.os.IInterface;
  import android.os.RemoteException;
  import android.provider.MediaStore;
  import android.text.TextUtils;
  import android.util.TypedValue;
  
  import com.lody.virtual.client.NativeEngine;
  import com.lody.virtual.client.VClientImpl;
  import com.lody.virtual.client.badger.BadgerManager;
  import com.lody.virtual.client.core.VirtualCore;
  import com.lody.virtual.client.env.Constants;
  import com.lody.virtual.client.env.SpecialComponentList;
  import com.lody.virtual.client.hook.base.MethodProxy;
  import com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;
  import com.lody.virtual.client.hook.delegate.TaskDescriptionDelegate;
  import com.lody.virtual.client.hook.providers.ProviderHook;
  import com.lody.virtual.client.hook.secondary.ServiceConnectionDelegate;
  import com.lody.virtual.client.hook.utils.MethodParameterUtils;
  import com.lody.virtual.client.ipc.ActivityClientRecord;
  import com.lody.virtual.client.ipc.VActivityManager;
  import com.lody.virtual.client.ipc.VNotificationManager;
  import com.lody.virtual.client.ipc.VPackageManager;
  import com.lody.virtual.client.stub.ChooserActivity;
  import com.lody.virtual.client.stub.StubPendingActivity;
  import com.lody.virtual.client.stub.StubPendingReceiver;
  import com.lody.virtual.client.stub.StubPendingService;
  import com.lody.virtual.client.stub.VASettings;
  import com.lody.virtual.helper.compat.ActivityManagerCompat;
  import com.lody.virtual.helper.compat.BuildCompat;
  import com.lody.virtual.helper.utils.ArrayUtils;
  import com.lody.virtual.helper.utils.BitmapUtils;
  import com.lody.virtual.helper.utils.ComponentUtils;
  import com.lody.virtual.helper.utils.DrawableUtils;
  import com.lody.virtual.helper.utils.EncodeUtils;
  import com.lody.virtual.helper.utils.FileUtils;
  import com.lody.virtual.helper.utils.Reflect;
  import com.lody.virtual.helper.utils.VLog;
  import com.lody.virtual.os.VUserHandle;
  import com.lody.virtual.os.VUserInfo;
  import com.lody.virtual.remote.AppTaskInfo;
  import com.lody.virtual.server.interfaces.IAppRequestListener;
  
  import java.io.File;
  import java.lang.ref.WeakReference;
  import java.lang.reflect.Method;
  import java.util.List;
  import java.util.WeakHashMap;
  
  import mirror.android.app.IActivityManager;
  import mirror.android.app.LoadedApk;
  import mirror.android.content.ContentProviderHolderOreo;
  import mirror.android.content.IIntentReceiverJB;
  import mirror.android.content.pm.UserInfo;
  
  /**
   * @author Lody
   */
  @SuppressWarnings("unused")
  class MethodProxies {
  
  
      static class ForceStopPackage extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "forceStopPackage";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              String pkg = (String) args[0];
              int userId = VUserHandle.myUserId();
              VActivityManager.get().killAppByPkg(pkg, userId);
              return 0;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class CrashApplication extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "crashApplication";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class AddPackageDependency extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "addPackageDependency";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              MethodParameterUtils.replaceFirstAppPkg(args);
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
      static class GetPackageForToken extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getPackageForToken";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IBinder token = (IBinder) args[0];
              String pkg = VActivityManager.get().getPackageForToken(token);
              if (pkg != null) {
                  return pkg;
              }
              return super.call(who, method, args);
          }
      }
  
      static class UnbindService extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "unbindService";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IServiceConnection conn = (IServiceConnection) args[0];
              ServiceConnectionDelegate delegate = ServiceConnectionDelegate.removeDelegate(conn);
              if (delegate == null) {
                  return method.invoke(who, args);
              }
              return VActivityManager.get().unbindService(delegate);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess() || isServerProcess();
          }
      }
  
      static class GetContentProviderExternal extends GetContentProvider {
  
          @Override
          public String getMethodName() {
              return "getContentProviderExternal";
          }
  
          @Override
          public int getProviderNameIndex() {
              return 0;
          }
  
          @Override
          public int getPackageIndex() {
              return -1;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
      static class StartVoiceActivity extends StartActivity {
          @Override
          public String getMethodName() {
              return "startVoiceActivity";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              return super.call(who, method, args);
          }
      }
  
  
      static class UnstableProviderDied extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "unstableProviderDied";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              if (args[0] == null) {
                  return 0;
              }
              return method.invoke(who, args);
          }
      }
  
  
      static class PeekService extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "peekService";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              MethodParameterUtils.replaceLastAppPkg(args);
              Intent service = (Intent) args[0];
              String resolvedType = (String) args[1];
              return VActivityManager.get().peekService(service, resolvedType);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetPackageAskScreenCompat extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getPackageAskScreenCompat";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
  
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                  if (args.length > 0 && args[0] instanceof String) {
                      args[0] = getHostPkg();
                  }
              }
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetIntentSender extends MethodProxy {
  
          protected int mIntentIndex = 5;
          protected int mResolvedTypesIndex = 6;
          protected int mFlagsIndex = 7;
  
          @Override
          public String getMethodName() {
              return "getIntentSender";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              String creator = (String) args[1];
              String[] resolvedTypes = (String[]) args[mResolvedTypesIndex];
              int type = (int) args[0];
              int flags = (int) args[mFlagsIndex];
              if (args[5] instanceof Intent[]) {
                  Intent[] intents = (Intent[]) args[mIntentIndex];
                  for (int i = 0; i < intents.length; i++) {
                      Intent intent = intents[i];
                      if (resolvedTypes != null && i < resolvedTypes.length) {
                          intent.setDataAndType(intent.getData(), resolvedTypes[i]);
                      }
                      Intent targetIntent = redirectIntentSender(type, creator, intent);
                      if (targetIntent != null) {
                          intents[i] = targetIntent;
                      }
                  }
              }
              args[mFlagsIndex] = flags;
              args[1] = getHostPkg();
              // Force userId to 0
              if (args[args.length - 1] instanceof Integer) {
                  args[args.length - 1] = 0;
              }
              IInterface sender = (IInterface) method.invoke(who, args);
              if (sender != null && creator != null) {
                  VActivityManager.get().addPendingIntent(sender.asBinder(), creator);
              }
              return sender;
          }
  
          private Intent redirectIntentSender(int type, String creator, Intent intent) {
              Intent newIntent = intent.cloneFilter();
              switch (type) {
                  case ActivityManagerCompat.INTENT_SENDER_ACTIVITY: {
                      ComponentInfo info = VirtualCore.get().resolveActivityInfo(intent, VUserHandle.myUserId());
                      if (info != null) {
                          newIntent.setClass(getHostContext(), StubPendingActivity.class);
                          newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                      }
                  }
                  break;
                  case ActivityManagerCompat.INTENT_SENDER_SERVICE: {
                      ComponentInfo info = VirtualCore.get().resolveServiceInfo(intent, VUserHandle.myUserId());
                      if (info != null) {
                          newIntent.setClass(getHostContext(), StubPendingService.class);
                      }
                  }
                  break;
                  case ActivityManagerCompat.INTENT_SENDER_BROADCAST: {
                      newIntent.setClass(getHostContext(), StubPendingReceiver.class);
                  }
                  break;
                  default:
                      return null;
              }
              newIntent.putExtra("_VA_|_user_id_", VUserHandle.myUserId());
              newIntent.putExtra("_VA_|_intent_", intent);
              newIntent.putExtra("_VA_|_creator_", creator);
              newIntent.putExtra("_VA_|_from_inner_", true);
              return newIntent;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
  
      }
  
  
      static class OverridePendingTransition extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "overridePendingTransition";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              // Many application crash/darkscreen if not return null.
              return null;
          }
      }
  
      static class StartActivity extends MethodProxy {
  
          private static final String SCHEME_FILE = "file";
          private static final String SCHEME_PACKAGE = "package";
          private static final String SCHEME_CONTENT = "content";
  
          @Override
          public String getMethodName() {
              return "startActivity";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              int intentIndex = ArrayUtils.indexOfObject(args, Intent.class, 1);
              if (intentIndex < 0) {
                  return ActivityManagerCompat.START_INTENT_NOT_RESOLVED;
              }
              int resultToIndex = ArrayUtils.indexOfObject(args, IBinder.class, 2);
              String resolvedType = (String) args[intentIndex + 1];
              Intent intent = (Intent) args[intentIndex];
              intent.setDataAndType(intent.getData(), resolvedType);
              IBinder resultTo = resultToIndex >= 0 ? (IBinder) args[resultToIndex] : null;
              int userId = VUserHandle.myUserId();
  
              if (ComponentUtils.isStubComponent(intent)) {
                  return method.invoke(who, args);
              }
  
              if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())
                      || (Intent.ACTION_VIEW.equals(intent.getAction())
                      && "application/vnd.android.package-archive".equals(intent.getType()))) {
                  if (handleInstallRequest(intent)) {
                      return 0;
                  }
              } else if ((Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())
                      || Intent.ACTION_DELETE.equals(intent.getAction()))
                      && "package".equals(intent.getScheme())) {
  
                  if (handleUninstallRequest(intent)) {
                      return 0;
                  }
              } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(intent.getAction()) ||
                      MediaStore.ACTION_VIDEO_CAPTURE.equals(intent.getAction()) ||
                      MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(intent.getAction())) {
                  handleMediaCaptureRequest(intent);
              }
  
              String resultWho = null;
              int requestCode = 0;
              Bundle options = ArrayUtils.getFirst(args, Bundle.class);
              if (resultTo != null) {
                  resultWho = (String) args[resultToIndex + 1];
                  requestCode = (int) args[resultToIndex + 2];
              }
              // chooser
              if (ChooserActivity.check(intent)) {
                  intent.setComponent(new ComponentName(getHostContext(), ChooserActivity.class));
                  intent.putExtra(Constants.EXTRA_USER_HANDLE, userId);
                  intent.putExtra(ChooserActivity.EXTRA_DATA, options);
                  intent.putExtra(ChooserActivity.EXTRA_WHO, resultWho);
                  intent.putExtra(ChooserActivity.EXTRA_REQUEST_CODE, requestCode);
                  return method.invoke(who, args);
              }
  
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                  args[intentIndex - 1] = getHostPkg();
              }
              if (intent.getScheme() != null && intent.getScheme().equals(SCHEME_PACKAGE) && intent.getData() != null) {
                  if (intent.getAction() != null && intent.getAction().startsWith("android.settings.")) {
                      intent.setData(Uri.parse("package:" + getHostPkg()));
                  }
              }
  
              ActivityInfo activityInfo = VirtualCore.get().resolveActivityInfo(intent, userId);
              if (activityInfo == null) {
                  VLog.e("VActivityManager", "Unable to resolve activityInfo : %s", intent);
                  if (intent.getPackage() != null && isAppPkg(intent.getPackage())) {
                      return ActivityManagerCompat.START_INTENT_NOT_RESOLVED;
                  }
                  
                  if (Build.VERSION.SDK_INT >= 30) {
                      args[1] = VirtualCore.get().getContext().getPackageName();
                  }
                  
                  return method.invoke(who, args);
              }
              int res = VActivityManager.get().startActivity(intent, activityInfo, resultTo, options, resultWho, requestCode, VUserHandle.myUserId());
              if (res != 0 && resultTo != null && requestCode > 0) {
                  VActivityManager.get().sendActivityResult(resultTo, resultWho, requestCode);
              }
              if (resultTo != null) {
                  ActivityClientRecord r = VActivityManager.get().getActivityRecord(resultTo);
                  if (r != null && r.activity != null) {
                      try {
                          TypedValue out = new TypedValue();
                          Resources.Theme theme = r.activity.getResources().newTheme();
                          theme.applyStyle(activityInfo.getThemeResource(), true);
                          if (theme.resolveAttribute(android.R.attr.windowAnimationStyle, out, true)) {
  
                              TypedArray array = theme.obtainStyledAttributes(out.data,
                                      new int[]{
                                              android.R.attr.activityOpenEnterAnimation,
                                              android.R.attr.activityOpenExitAnimation
                                      });
  
                              r.activity.overridePendingTransition(array.getResourceId(0, 0), array.getResourceId(1, 0));
                              array.recycle();
                          }
                      } catch (Throwable e) {
                          // Ignore
                      }
                  }
              }
              return res;
          }
  
  
          private boolean handleInstallRequest(Intent intent) {
              IAppRequestListener listener = VirtualCore.get().getAppRequestListener();
              if (listener != null) {
                  Uri packageUri = intent.getData();
                  String sourcePath = FileUtils.getFileFromUri(getHostContext(), packageUri);
                  try {
                      listener.onRequestInstall(sourcePath);
                      return true;
                  } catch (RemoteException e) {
                      e.printStackTrace();
                  }
              }
              return false;
          }
  
          private boolean handleUninstallRequest(Intent intent) {
              IAppRequestListener listener = VirtualCore.get().getAppRequestListener();
              if (listener != null) {
                  Uri packageUri = intent.getData();
                  if (SCHEME_PACKAGE.equals(packageUri.getScheme())) {
                      String pkg = packageUri.getSchemeSpecificPart();
                      try {
                          listener.onRequestUninstall(pkg);
                          return true;
                      } catch (RemoteException e) {
                          e.printStackTrace();
                      }
                  }
  
              }
              return false;
          }
  
          private void handleMediaCaptureRequest(Intent intent) {
              Uri uri = intent.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
              if (uri == null || !SCHEME_FILE.equals(uri.getScheme())) {
                  return;
              }
              String path = uri.getPath();
              String newPath = NativeEngine.getRedirectedPath(path);
              if (newPath == null) {
                  return;
              }
              File realFile = new File(newPath);
              Uri newUri = Uri.fromFile(realFile);
              intent.putExtra(MediaStore.EXTRA_OUTPUT, newUri);
          }
  
      }
  
      static class StartActivities extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "startActivities";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              Intent[] intents = ArrayUtils.getFirst(args, Intent[].class);
              String[] resolvedTypes = ArrayUtils.getFirst(args, String[].class);
              IBinder token = null;
              int tokenIndex = ArrayUtils.indexOfObject(args, IBinder.class, 2);
              if (tokenIndex != -1) {
                  token = (IBinder) args[tokenIndex];
              }
              Bundle options = ArrayUtils.getFirst(args, Bundle.class);
              return VActivityManager.get().startActivities(intents, resolvedTypes, token, options, VUserHandle.myUserId());
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class FinishActivity extends MethodProxy {
          @Override
          public String getMethodName() {
              return "finishActivity";
          }
  
          @Override
          public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {
              IBinder token = (IBinder) args[0];
              ActivityClientRecord r = VActivityManager.get().getActivityRecord(token);
              boolean taskRemoved = VActivityManager.get().onActivityDestroy(token);
              if (!taskRemoved && r != null && r.activity != null && r.info.getThemeResource() != 0) {
                  try {
                      TypedValue out = new TypedValue();
                      Resources.Theme theme = r.activity.getResources().newTheme();
                      theme.applyStyle(r.info.getThemeResource(), true);
                      if (theme.resolveAttribute(android.R.attr.windowAnimationStyle, out, true)) {
  
                          TypedArray array = theme.obtainStyledAttributes(out.data,
                                  new int[]{
                                          android.R.attr.activityCloseEnterAnimation,
                                          android.R.attr.activityCloseExitAnimation
                                  });
                          r.activity.overridePendingTransition(array.getResourceId(0, 0), array.getResourceId(1, 0));
                          array.recycle();
                      }
                  } catch (Throwable e) {
                      e.printStackTrace();
                  }
              }
              return super.afterCall(who, method, args, result);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetCallingPackage extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getCallingPackage";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IBinder token = (IBinder) args[0];
              return VActivityManager.get().getCallingPackage(token);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetPackageForIntentSender extends MethodProxy {
          @Override
          public String getMethodName() {
              return "getPackageForIntentSender";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IInterface sender = (IInterface) args[0];
              if (sender != null) {
                  String packageName = VActivityManager.get().getPackageForIntentSender(sender.asBinder());
                  if (packageName != null) {
                      return packageName;
                  }
              }
              return super.call(who, method, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      @SuppressWarnings("unchecked")
      static class PublishContentProviders extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "publishContentProviders";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetServices extends MethodProxy {
          @Override
          public String getMethodName() {
              return "getServices";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              int maxNum = (int) args[0];
              int flags = (int) args[1];
              return VActivityManager.get().getServices(maxNum, flags).getList();
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
      static class GrantUriPermissionFromOwner extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "grantUriPermissionFromOwner";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              MethodParameterUtils.replaceFirstAppPkg(args);
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
      static class SetServiceForeground extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "setServiceForeground";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              ComponentName component = (ComponentName) args[0];
              IBinder token = (IBinder) args[1];
              int id = (int) args[2];
              Notification notification = (Notification) args[3];
              boolean removeNotification = false;
              if (args[4] instanceof Boolean) {
                  removeNotification = (boolean) args[4];
              } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && args[4] instanceof Integer) {
                  int flags = (int) args[4];
                  removeNotification = (flags & Service.STOP_FOREGROUND_REMOVE) != 0;
              } else {
                  VLog.e(getClass().getSimpleName(), "Unknown flag : " + args[4]);
              }
              VNotificationManager.get().dealNotification(id, notification, getAppPkg());
  
              /**
               * `BaseStatusBar#updateNotification` aosp will use use
               * `new StatusBarIcon(...notification.getSmallIcon()...)`
               *  while in samsung SystemUI.apk ,the corresponding code comes as
               * `new StatusBarIcon(...pkgName,notification.icon...)`
               * the icon comes from `getSmallIcon.getResource`
               * which will throw an exception on :x process thus crash the application
               */
              if (notification != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
                      (Build.BRAND.equalsIgnoreCase("samsung") || Build.MANUFACTURER.equalsIgnoreCase("samsung"))) {
                  notification.icon = getHostContext().getApplicationInfo().icon;
                  Icon icon = Icon.createWithResource(getHostPkg(), notification.icon);
                  Reflect.on(notification).call("setSmallIcon", icon);
              }
  
              VActivityManager.get().setServiceForeground(component, token, id, notification, removeNotification);
              return 0;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class UpdateDeviceOwner extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "updateDeviceOwner";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              MethodParameterUtils.replaceFirstAppPkg(args);
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
  
      }
  
  
      static class GetIntentForIntentSender extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getIntentForIntentSender";
          }
  
          @Override
          public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {
              Intent intent = (Intent) super.afterCall(who, method, args, result);
              if (intent != null && intent.hasExtra("_VA_|_intent_")) {
                  return intent.getParcelableExtra("_VA_|_intent_");
              }
              return intent;
          }
      }
  
  
      static class UnbindFinished extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "unbindFinished";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IBinder token = (IBinder) args[0];
              Intent service = (Intent) args[1];
              boolean doRebind = (boolean) args[2];
              VActivityManager.get().unbindFinished(token, service, doRebind);
              return 0;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
      static class StartActivityIntentSender extends MethodProxy {
          @Override
          public String getMethodName() {
              return "startActivityIntentSender";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
  
              return super.call(who, method, args);
          }
      }
  
  
      static class BindService extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "bindService";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IInterface caller = (IInterface) args[0];
              IBinder token = (IBinder) args[1];
              Intent service = (Intent) args[2];
              String resolvedType = (String) args[3];
              IServiceConnection conn = (IServiceConnection) args[4];
              int flags = (int) args[5];
              int userId = VUserHandle.myUserId();
              if (isServerProcess()) {
                  userId = service.getIntExtra("_VA_|_user_id_", VUserHandle.USER_NULL);
              }
              if (userId == VUserHandle.USER_NULL) {
                  return method.invoke(who, args);
              }
              ServiceInfo serviceInfo = VirtualCore.get().resolveServiceInfo(service, userId);
              if (serviceInfo != null) {
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                      service.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
                  }
                  conn = ServiceConnectionDelegate.getDelegate(conn);
                  return VActivityManager.get().bindService(caller.asBinder(), token, service, resolvedType,
                          conn, flags, userId);
              }
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess() || isServerProcess();
          }
      }
  
      // http://aospxref.com/android-10.0.0_r2/xref/frameworks/base/core/java/android/app/ContextImpl.java#1735
      static class BindIsolatedService extends BindService {
          @Override
          public String getMethodName() {
              return "bindIsolatedService";
          }
  
          @Override
          public boolean beforeCall(Object who, Method method, Object... args) {
              MethodParameterUtils.replaceLastAppPkg(args);
              return super.beforeCall(who, method, args);
          }
      }
  
  
      static class StartService extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "startService";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IInterface appThread = (IInterface) args[0];
              Intent service = (Intent) args[1];
              String resolvedType = (String) args[2];
              if (service.getComponent() != null
                      && getHostPkg().equals(service.getComponent().getPackageName())) {
                  // for server process
                  return method.invoke(who, args);
              }
              int userId = VUserHandle.myUserId();
              if (service.getBooleanExtra("_VA_|_from_inner_", false)) {
                  userId = service.getIntExtra("_VA_|_user_id_", userId);
                  service = service.getParcelableExtra("_VA_|_intent_");
              } else {
                  if (isServerProcess()) {
                      userId = service.getIntExtra("_VA_|_user_id_", VUserHandle.USER_NULL);
                  }
              }
              service.setDataAndType(service.getData(), resolvedType);
              ServiceInfo serviceInfo = VirtualCore.get().resolveServiceInfo(service, VUserHandle.myUserId());
              if (serviceInfo != null) {
                  if (isFiltered(service)) {
                      return service.getComponent();
                  }
                  return VActivityManager.get().startService(appThread, service, resolvedType, userId);
              }
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess() || isServerProcess();
          }
  
          private boolean isFiltered(Intent service) {
              // disable tinker.
              if (service != null && service.getComponent() != null
                      && EncodeUtils.decode("Y29tLnRlbmNlbnQudGlua2VyLmxpYi5zZXJ2aWMuVGlua2VyUGF0Y2hTZXJ2aWNl") // com.tencent.tinker.lib.service.TinkerPatchService
                      .equals(service.getComponent().getClassName())) {
                  return true;
              }
              return false;
          }
      }
  
      static class StartActivityAndWait extends StartActivity {
          @Override
          public String getMethodName() {
              return "startActivityAndWait";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              return super.call(who, method, args);
          }
      }
  
  
      static class PublishService extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "publishService";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IBinder token = (IBinder) args[0];
              if (!VActivityManager.get().isVAServiceToken(token)) {
                  return method.invoke(who, args);
              }
              Intent intent = (Intent) args[1];
              IBinder service = (IBinder) args[2];
              VActivityManager.get().publishService(token, intent, service);
              return 0;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      @SuppressWarnings("unchecked")
      static class GetRunningAppProcesses extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getRunningAppProcesses";
          }
  
          @Override
          public synchronized Object call(Object who, Method method, Object... args) throws Throwable {
              List<ActivityManager.RunningAppProcessInfo> infoList = (List<ActivityManager.RunningAppProcessInfo>) method
                      .invoke(who, args);
              if (infoList != null) {
                  for (ActivityManager.RunningAppProcessInfo info : infoList) {
                      if (VActivityManager.get().isAppPid(info.pid)) {
                          List<String> pkgList = VActivityManager.get().getProcessPkgList(info.pid);
                          String processName = VActivityManager.get().getAppProcessName(info.pid);
                          if (processName != null) {
                              info.processName = processName;
                          }
                          info.pkgList = pkgList.toArray(new String[pkgList.size()]);
                          info.uid = VUserHandle.getAppId(VActivityManager.get().getUidByPid(info.pid));
                      }
                  }
              }
              return infoList;
          }
      }
  
  
      static class SetPackageAskScreenCompat extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "setPackageAskScreenCompat";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
  
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                  if (args.length > 0 && args[0] instanceof String) {
                      args[0] = getHostPkg();
                  }
              }
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetCallingActivity extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getCallingActivity";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IBinder token = (IBinder) args[0];
              return VActivityManager.get().getCallingActivity(token);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetCurrentUser extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getCurrentUser";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              try {
                  return UserInfo.ctor.newInstance(0, "user", VUserInfo.FLAG_PRIMARY);
              } catch (Throwable e) {
                  e.printStackTrace();
              }
              return null;
          }
      }
  
  
      static class KillApplicationProcess extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "killApplicationProcess";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              if (args.length > 1 && args[0] instanceof String && args[1] instanceof Integer) {
                  String processName = (String) args[0];
                  int uid = (int) args[1];
                  VActivityManager.get().killApplicationProcess(processName, uid);
                  return 0;
              }
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class StartActivityAsUser extends StartActivity {
  
          @Override
          public String getMethodName() {
              return "startActivityAsUser";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              return super.call(who, method, args);
          }
      }
  
  
      static class CheckPermission extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "checkPermission";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              String permission = (String) args[0];
              if (SpecialComponentList.isWhitePermission(permission)) {
                  return PackageManager.PERMISSION_GRANTED;
              }
              if (permission.startsWith("com.google")) {
                  return PackageManager.PERMISSION_GRANTED;
              }
              args[args.length - 1] = getRealUid();
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
  
      }
  
  
      static class StartActivityAsCaller extends StartActivity {
  
          @Override
          public String getMethodName() {
              return "startActivityAsCaller";
          }
      }
  
  
      static class HandleIncomingUser extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "handleIncomingUser";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              int lastIndex = args.length - 1;
              if (args[lastIndex] instanceof String) {
                  args[lastIndex] = getHostPkg();
              }
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
  
      }
  
  
      @SuppressWarnings("unchecked")
      static class GetTasks extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getTasks";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              List<ActivityManager.RunningTaskInfo> runningTaskInfos = (List<ActivityManager.RunningTaskInfo>) method
                      .invoke(who, args);
              for (ActivityManager.RunningTaskInfo info : runningTaskInfos) {
                  AppTaskInfo taskInfo = VActivityManager.get().getTaskInfo(info.id);
                  if (taskInfo != null) {
                      info.topActivity = taskInfo.topActivity;
                      info.baseActivity = taskInfo.baseActivity;
                  }
              }
              return runningTaskInfos;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetPersistedUriPermissions extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getPersistedUriPermissions";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              MethodParameterUtils.replaceFirstAppPkg(args);
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class RegisterReceiver extends MethodProxy {
          protected int mIIntentReceiverIndex = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
                  ? 2
                  : 1;
  
          protected int mRequiredPermissionIndex = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
                  ? 4
                  : 3;
          protected int mIntentFilterIndex = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
                  ? 3
                  : 2;
  
          private WeakHashMap<IBinder, IIntentReceiver> mProxyIIntentReceivers = new WeakHashMap<>();
  
          @Override
          public String getMethodName() {
              return "registerReceiver";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              MethodParameterUtils.replaceFirstAppPkg(args);
              args[mRequiredPermissionIndex] = null;
              IntentFilter filter = (IntentFilter) args[mIntentFilterIndex];
              SpecialComponentList.protectIntentFilter(filter);
              if (args.length > mIIntentReceiverIndex && IIntentReceiver.class.isInstance(args[mIIntentReceiverIndex])) {
                  final IInterface old = (IInterface) args[mIIntentReceiverIndex];
                  if (!IIntentReceiverProxy.class.isInstance(old)) {
                      final IBinder token = old.asBinder();
                      if (token != null) {
                          token.linkToDeath(new IBinder.DeathRecipient() {
                              @Override
                              public void binderDied() {
                                  token.unlinkToDeath(this, 0);
                                  mProxyIIntentReceivers.remove(token);
                              }
                          }, 0);
                          IIntentReceiver proxyIIntentReceiver = mProxyIIntentReceivers.get(token);
                          if (proxyIIntentReceiver == null) {
                              proxyIIntentReceiver = new IIntentReceiverProxy(old);
                              mProxyIIntentReceivers.put(token, proxyIIntentReceiver);
                          }
                          WeakReference mDispatcher = LoadedApk.ReceiverDispatcher.InnerReceiver.mDispatcher.get(old);
                          if (mDispatcher != null) {
                              LoadedApk.ReceiverDispatcher.mIIntentReceiver.set(mDispatcher.get(), proxyIIntentReceiver);
                              args[mIIntentReceiverIndex] = proxyIIntentReceiver;
                          }
                      }
                  }
              }
              return method.invoke(who, args);
          }
  
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
  
          private static class IIntentReceiverProxy extends IIntentReceiver.Stub {
  
              IInterface mOld;
  
              IIntentReceiverProxy(IInterface old) {
                  this.mOld = old;
              }
  
              public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered,
                                         boolean sticky, int sendingUser) throws RemoteException {
                  if (!accept(intent)) {
                      return;
                  }
                  if (intent.hasExtra("_VA_|_intent_")) {
                      intent = intent.getParcelableExtra("_VA_|_intent_");
                  }
                  SpecialComponentList.unprotectIntent(intent);
                  if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
                      IIntentReceiverJB.performReceive.call(mOld, intent, resultCode, data, extras, ordered, sticky, sendingUser);
                  } else {
                      mirror.android.content.IIntentReceiver.performReceive.call(mOld, intent, resultCode, data, extras, ordered, sticky);
                  }
              }
  
              private boolean accept(Intent intent) {
                  int uid = intent.getIntExtra("_VA_|_uid_", -1);
                  if (uid != -1) {
                      return VClientImpl.get().getVUid() == uid;
                  }
                  int userId = intent.getIntExtra("_VA_|_user_id_", -1);
                  return userId == -1 || userId == VUserHandle.myUserId();
              }
  
              @SuppressWarnings("unused")
              public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered,
                                         boolean sticky) throws RemoteException {
                  this.performReceive(intent, resultCode, data, extras, ordered, sticky, 0);
              }
  
          }
      }
  
  
      static class StopService extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "stopService";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IInterface caller = (IInterface) args[0];
              Intent intent = (Intent) args[1];
              String resolvedType = (String) args[2];
              intent.setDataAndType(intent.getData(), resolvedType);
              ComponentName componentName = intent.getComponent();
              PackageManager pm = VirtualCore.getPM();
              if (componentName == null) {
                  ResolveInfo resolveInfo = pm.resolveService(intent, 0);
                  if (resolveInfo != null && resolveInfo.serviceInfo != null) {
                      componentName = new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
                  }
              }
              if (componentName != null && !getHostPkg().equals(componentName.getPackageName())) {
                  return VActivityManager.get().stopService(caller, intent, resolvedType);
              }
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess() || isServerProcess();
          }
      }
  
  
      static class GetContentProvider extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getContentProvider";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              int nameIdx = getProviderNameIndex();
              String name = (String) args[nameIdx];
              int userId = VUserHandle.myUserId();
              ProviderInfo info = VPackageManager.get().resolveContentProvider(name, 0, userId);
              if (info != null && info.enabled && isAppPkg(info.packageName)) {
                  int targetVPid = VActivityManager.get().initProcess(info.packageName, info.processName, userId);
                  if (targetVPid == -1) {
                      return null;
                  }
                  args[nameIdx] = VASettings.getStubAuthority(targetVPid);
                  
                  if (Build.VERSION.SDK_INT >= 30) {
                      args[1] = VirtualCore.get().getContext().getPackageName();
                  }
                  
                  Object holder = method.invoke(who, args);
                  if (holder == null) {
                      return null;
                  }
                  if (BuildCompat.isOreo()) {
                      IInterface provider = ContentProviderHolderOreo.provider.get(holder);
                      if (provider != null) {
                          provider = VActivityManager.get().acquireProviderClient(userId, info);
                      }
                      ContentProviderHolderOreo.provider.set(holder, provider);
                      ContentProviderHolderOreo.info.set(holder, info);
                  } else {
                      IInterface provider = IActivityManager.ContentProviderHolder.provider.get(holder);
                      if (provider != null) {
                          provider = VActivityManager.get().acquireProviderClient(userId, info);
                      }
                      IActivityManager.ContentProviderHolder.provider.set(holder, provider);
                      IActivityManager.ContentProviderHolder.info.set(holder, info);
                  }
                  return holder;
              }
  
              if (BuildCompat.isQ()) {
                  int packageIndex = getPackageIndex();
                  if (packageIndex > 0 && args[packageIndex] instanceof String) {
                      args[packageIndex] = getHostPkg();
                  }
              }
              Object holder = method.invoke(who, args);
              if (holder != null) {
                  if (BuildCompat.isOreo()) {
                      IInterface provider = ContentProviderHolderOreo.provider.get(holder);
                      info = ContentProviderHolderOreo.info.get(holder);
                      if (provider != null) {
                          provider = ProviderHook.createProxy(true, info.authority, provider);
                      }
                      ContentProviderHolderOreo.provider.set(holder, provider);
                  } else {
                      IInterface provider = IActivityManager.ContentProviderHolder.provider.get(holder);
                      info = IActivityManager.ContentProviderHolder.info.get(holder);
                      if (provider != null) {
                          provider = ProviderHook.createProxy(true, info.authority, provider);
                      }
                      IActivityManager.ContentProviderHolder.provider.set(holder, provider);
                  }
                  return holder;
              }
              return null;
          }
  
  
          public int getProviderNameIndex() {
              if (BuildCompat.isQ()) {
                  return 2;
              }
              return 1;
          }
  
          public int getPackageIndex() {
              if (BuildCompat.isQ()) {
                  return 1;
              }
              return -1;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
      @TargetApi(Build.VERSION_CODES.LOLLIPOP)
      static class SetTaskDescription extends MethodProxy {
          @Override
          public String getMethodName() {
              return "setTaskDescription";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              ActivityManager.TaskDescription td = (ActivityManager.TaskDescription) args[1];
              String label = td.getLabel();
              Bitmap icon = td.getIcon();
  
              // If the activity label/icon isn't specified, the application's label/icon is shown instead
              // Android usually does that for us, but in this case we want info about the contained app, not VIrtualApp itself
              if (label == null || icon == null) {
                  Application app = VClientImpl.get().getCurrentApplication();
                  if (app != null) {
                      try {
                          if (label == null) {
                              label = app.getApplicationInfo().loadLabel(app.getPackageManager()).toString();
                          }
                          if (icon == null) {
                              Drawable drawable = app.getApplicationInfo().loadIcon(app.getPackageManager());
                              if (drawable != null) {
                                  icon = DrawableUtils.drawableToBitMap(drawable);
                              }
                          }
                          td = new ActivityManager.TaskDescription(label, icon, td.getPrimaryColor());
                      } catch (Throwable e) {
                          e.printStackTrace();
                      }
                  }
              }
  
              TaskDescriptionDelegate descriptionDelegate = VirtualCore.get().getTaskDescriptionDelegate();
              if (descriptionDelegate != null) {
                  td = descriptionDelegate.getTaskDescription(td);
              }
  
              args[1] = td;
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
      static class StopServiceToken extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "stopServiceToken";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              ComponentName componentName = (ComponentName) args[0];
              IBinder token = (IBinder) args[1];
              if (!VActivityManager.get().isVAServiceToken(token)) {
                  return method.invoke(who, args);
              }
              int startId = (int) args[2];
              if (componentName != null) {
                  return VActivityManager.get().stopServiceToken(componentName, token, startId);
              }
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess() || isServerProcess();
          }
      }
  
      static class StartActivityWithConfig extends StartActivity {
          @Override
          public String getMethodName() {
              return "startActivityWithConfig";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              return super.call(who, method, args);
          }
      }
  
      static class StartNextMatchingActivity extends StartActivity {
          @Override
          public String getMethodName() {
              return "startNextMatchingActivity";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              return false;
          }
      }
  
  
      static class BroadcastIntent extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "broadcastIntent";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              Intent intent = (Intent) args[1];
              String type = (String) args[2];
              intent.setDataAndType(intent.getData(), type);
              if (VirtualCore.get().getComponentDelegate() != null) {
                  VirtualCore.get().getComponentDelegate().onSendBroadcast(intent);
              }
              Intent newIntent = handleIntent(intent);
              if (newIntent != null) {
                  args[1] = newIntent;
              } else {
                  return 0;
              }
  
              if (args[7] instanceof String || args[7] instanceof String[]) {
                  // clear the permission
                  args[7] = null;
              }
              return method.invoke(who, args);
          }
  
  
          private Intent handleIntent(final Intent intent) {
              final String action = intent.getAction();
              if ("android.intent.action.CREATE_SHORTCUT".equals(action)
                      || "com.android.launcher.action.INSTALL_SHORTCUT".equals(action)) {
  
                  return VASettings.ENABLE_INNER_SHORTCUT ? handleInstallShortcutIntent(intent) : null;
  
              } else if ("com.android.launcher.action.UNINSTALL_SHORTCUT".equals(action)) {
  
                  handleUninstallShortcutIntent(intent);
  
              } else if (BadgerManager.handleBadger(intent)) {
                  return null;
              } else if (Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)) {
                  // intent send to system, do not modify it's action(may have other same intent)
                  return handleMediaScannerIntent(intent);
              } else {
                  return ComponentUtils.redirectBroadcastIntent(intent, VUserHandle.myUserId());
              }
              return intent;
          }
  
          private Intent handleMediaScannerIntent(Intent intent) {
              if (intent == null) {
                  return null;
              }
              Uri data = intent.getData();
              if (data == null) {
                  return intent;
              }
              String scheme = data.getScheme();
              if (!"file".equalsIgnoreCase(scheme)) {
                  return intent;
              }
              String path = data.getPath();
              if (path == null) {
                  return intent;
              }
              String newPath = NativeEngine.getRedirectedPath(path);
              File newFile = new File(newPath);
              if (!newFile.exists()) {
                  return intent;
              }
              intent.setData(Uri.fromFile(newFile));
              return intent;
          }
  
          private Intent handleInstallShortcutIntent(Intent intent) {
              Intent shortcut = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
              if (shortcut != null) {
                  ComponentName component = shortcut.resolveActivity(VirtualCore.getPM());
                  if (component != null) {
                      String pkg = component.getPackageName();
                      Intent newShortcutIntent = new Intent();
                      newShortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);
                      newShortcutIntent.addCategory(Intent.CATEGORY_DEFAULT);
                      newShortcutIntent.putExtra("_VA_|_intent_", shortcut);
                      newShortcutIntent.putExtra("_VA_|_uri_", shortcut.toUri(0));
                      newShortcutIntent.putExtra("_VA_|_user_id_", VUserHandle.myUserId());
                      intent.removeExtra(Intent.EXTRA_SHORTCUT_INTENT);
                      intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, newShortcutIntent);
  
                      Intent.ShortcutIconResource icon = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
                      if (icon != null && !TextUtils.equals(icon.packageName, getHostPkg())) {
                          try {
                              Resources resources = VirtualCore.get().getResources(pkg);
                              int resId = resources.getIdentifier(icon.resourceName, "drawable", pkg);
                              if (resId > 0) {
                                  //noinspection deprecation
                                  Drawable iconDrawable = resources.getDrawable(resId);
                                  Bitmap newIcon = BitmapUtils.drawableToBitmap(iconDrawable);
                                  if (newIcon != null) {
                                      intent.removeExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
                                      intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, newIcon);
                                  }
                              }
                          } catch (Throwable e) {
                              e.printStackTrace();
                          }
                      }
                  }
              }
              return intent;
          }
  
          private void handleUninstallShortcutIntent(Intent intent) {
              Intent shortcut = intent.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
              if (shortcut != null) {
                  ComponentName componentName = shortcut.resolveActivity(getPM());
                  if (componentName != null) {
                      Intent newShortcutIntent = new Intent();
                      newShortcutIntent.putExtra("_VA_|_uri_", shortcut.toUri(0));
                      newShortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);
                      newShortcutIntent.removeExtra(Intent.EXTRA_SHORTCUT_INTENT);
                      intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, newShortcutIntent);
                  }
              }
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class GetActivityClassForToken extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "getActivityClassForToken";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IBinder token = (IBinder) args[0];
              return VActivityManager.get().getActivityForToken(token);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class CheckGrantUriPermission extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "checkGrantUriPermission";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              MethodParameterUtils.replaceFirstAppPkg(args);
              return method.invoke(who, args);
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
  
      static class ServiceDoneExecuting extends MethodProxy {
  
          @Override
          public String getMethodName() {
              return "serviceDoneExecuting";
          }
  
          @Override
          public Object call(Object who, Method method, Object... args) throws Throwable {
              IBinder token = (IBinder) args[0];
              if (!VActivityManager.get().isVAServiceToken(token)) {
                  return method.invoke(who, args);
              }
              int type = (int) args[1];
              int startId = (int) args[2];
              int res = (int) args[3];
              VActivityManager.get().serviceDoneExecuting(token, type, startId, res);
              return 0;
          }
  
          @Override
          public boolean isEnable() {
              return isAppProcess();
          }
      }
  
      static class GetPackageProcessState extends ReplaceLastPkgMethodProxy {
          public GetPackageProcessState() {
              super("getPackageProcessState");
          }
      }
  
      // For Android 11
      static class RegisterReceiverWithFeature extends RegisterReceiver {
          public RegisterReceiverWithFeature() {
              if (BuildCompat.isS()) {
                  // http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/java/android/app/IActivityManager.aidl?fi=IActivityManager#127
                  mIIntentReceiverIndex = 4;
                  mIntentFilterIndex = 5;
                  mRequiredPermissionIndex = 6;
              } else {
                  // http://aospxref.com/android-11.0.0_r21/xref/frameworks/base/core/java/android/app/IActivityManager.aidl?fi=IActivityManager#124
                  mIIntentReceiverIndex = 3;
                  mIntentFilterIndex = 4;
                  mRequiredPermissionIndex = 5;
              }
          }
  
          @Override
          public String getMethodName() {
              return "registerReceiverWithFeature";
          }
      }
  
      static class GetIntentSenderWithFeature extends GetIntentSender {
  
          public GetIntentSenderWithFeature() {
              // http://aospxref.com/android-11.0.0_r21/xref/frameworks/base/core/java/android/app/IActivityManager.aidl?fi=IActivityManager#245
              mIntentIndex = 6;
              mResolvedTypesIndex = 7;
              mFlagsIndex = 8;
          }
  
          @Override
          public String getMethodName() {
              return "getIntentSenderWithFeature";
          }
      }
  }
  