VirtualApp框架--- Application启动过程

VirtualApp源码分析——(一)VA启动 VirtualApp(以下简称VA)是一个开源的Android app虚拟化引擎,它提供了一个虚拟空间,允许其他应用运行在这个虚拟空间中,实现应用多开(可重复安装)。 在VA中,host应用(VA自身)和client应用(通过VA安装的应用)具有相同的uid。 在运行时,一般会包含三个进程: io.virtualapp,主进程,负责VA的用户界面及应用管理; io.virtualapp:... 阅读详情

一、VA 的Application为VApp,定义在io.virtualapp包中。VA重写了attachBaseContext()方法,在程序启动时会首先运行该方法,然后再调用VA的oncreate()方法。

二、VApp的attachBaseContext方法会去调用VirtualCore.getCore().startup(base)方法。

2.1. VirtualCore类定义在com.lody.virtual.client.core包中,getCore方法则是获取VirtualCore类的一个静态对象。接着调用该静态对象的startup方法。startup方法的部分源码如下

this.context = context;
            mainThread = ActivityThread.currentActivityThread();
            hostBindData = ActivityThreadCompat.getBoundApplication(mainThread);
            unHookPackageManager = context.getPackageManager();
            hostPkgInfo = unHookPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS);
            ProviderInfo[] hostProviders = hostPkgInfo.providers;
            if (hostProviders != null) {
                for (ProviderInfo info : hostProviders) {
                    hostProviderAuths.add(info.authority);

在上面代码中得出,首先获取当前的线程,然后获取这个线程的boundApplication对象,即AppBindData对象,在该对象中保存该线程的一些属性信息。然后调用context的getPackageManager方法获取了一个包管理器,然后通过传入当前包名获取当前的包信息,再然后就可以获取存储在报信息中的provider信息,最后将所有provider的authority属性保存到hostProviderAuths中。

2.2 接下来代码如下:

    // Host包名
            pkgName = context.getPackageName();
            // 主进程名
            mainProcessName = context.getApplicationInfo().processName;
            // 当前进程名
            processName = mainThread.getProcessName();
            if (processName.equals(mainProcessName)) {
                processType = ProcessType.Main;
            } else if (processName.endsWith(Constants.SERVER_PROCESS_NAME)) {
                processType = ProcessType.Server;
            } else if (LocalProcessManager.isAppProcess(processName)) {
                processType = ProcessType.VAppClient;
            } else {
                processType = ProcessType.CHILD;
            }
            PatchManager patchManager = PatchManager.getInstance();
            patchManager.injectAll();
            patchManager.checkEnv();
            RuntimeEnv.init();
            PatchManager.fixContext(context);
            isStartUp = true;

在上面的代码中首先获取当前包名,主进程名,当前进程名,然后判断当前进程的进程类型。进程类型分为四种,分别是服务端进程,插件客户端进程,主进程和子进程。前两张判断比较简单,直接比较当前进程名与主进程名是否相同可以判断是否为主进程;检查当前进程的名字是否以’:x’结尾可以判断是否为服务端进程。检查是否为插件客户端进程则需要调用LocalProcessManager类的isAppProcess方法。

2.3 接下来分析一下这个方法,这个方法的源码如下:

        public static boolean isAppProcess(String processName) {
        try {
            return getService().isAppProcess(processName);
        } catch (RemoteException e) {
            return RuntimeEnv.crash(e);
        }
    }

从上面代码可以看到它首先调用了getService方法,这个方法会返回一个IProcessManager对象,然后调用该对象的isAppProcess方法进行判断。

2.4 接下来看一下getService方法的源码

    private static IProcessManager service;

    private static IProcessManager getService() {
        if (service == null) {
            synchronized (LocalProcessManager.class) {
                if (service == null) {
                    IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.PROCESS_MANAGER);
                    service = IProcessManager.Stub.asInterface(binder);
                }
            }
        }
        if (service == null) {
            throw new RuntimeException("Unable to attach ProcessManager");
        }
        return service;
    }

在getService方法中,调用了ServiceManagerNative类的getService方法获取了一个IBinder对象,然后将远程对象的代理传给了service,然后进行了返回。

2.5 接下来看一下ServiceManagerNative类的getService方法,其源码如下:

        public static IBinder getService(String name) {
        if (VirtualCore.getCore().isServiceProcess()) {
            return ServiceCache.getService(name);
        }
        IServiceFetcher fetcher = getServiceFetcher();
        if (fetcher != null) {
            try {
                return fetcher.getService(name);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        VLog.e(TAG, "GetService(%s) return null.", name);
        return null;
    }

首先调用VirtualCore类中的isServiceProcess方法判断当前进程是否为服务进程,如果是的话直接返回保存在ServiceCache类中的service。从上文分析来看,如果当前进程是服务进程就会直接返回了,不会有2.3步骤的分析了,因此此处不是服务进程,进入下面分析。通过调用getServiceFetcher方法获取一个IServiceFetcher对象,然后调用返回该对象的getService方法。

2.6 接着看一下getServiceFetcher方法的源码:

    public static IServiceFetcher getServiceFetcher() {
        Context context = VirtualCore.getCore().getContext();
        Bundle response = new ProviderCaller.Builder(context, SERVICE_CP_AUTH).methodName("@").call();
        if (response != null) {
            IBinder binder = BundleCompat.getBinder(response, ExtraConstants.EXTRA_BINDER);
            linkBinderDied(binder);
            return IServiceFetcher.Stub.asInterface(binder);
        }
        return null;
    }

首先获取了VirtualCore中的context对象,然后构建了一个ProviderCaller的一个内部类Builder对象,然后依次调用了methodName方法和call方法,最后返回了一个Bundle对象。

2.7 首先看一下内部类Builder的源码:

public static final class Builder {

        private Context context;

        private Bundle bundle = new Bundle();

        private String methodName;
        private String auth;
        private String arg;

        public Builder(Context context, String auth) {
            this.context = context;
            this.auth = auth;
        }

        public Builder methodName(String name) {
            this.methodName = name;
            return this;
        }

        public Builder arg(String arg) {
            this.arg = arg;
            return this;
        }

        public Builder addArg(String key, Object value) {
            if (value != null) {
                if (value instanceof IBinder) {
                    if (Build.VERSION.SDK_INT >= 18) {
                        bundle.putBinder(key, (IBinder) value);
                    } else {
                        //noinspection deprecation
                        bundle.putIBinder(key, (IBinder) value);
                    }
                } else if (value instanceof Boolean) {
                    bundle.putBoolean(key, (Boolean) value);
                } else if (value instanceof Integer) {
                    bundle.putInt(key, (Integer) value);
                } else if (value instanceof String) {
                    bundle.putString(key, (String) value);
                } else if (value instanceof Serializable) {
                    bundle.putSerializable(key, (Serializable) value);
                } else if (value instanceof Bundle) {
                    bundle.putBundle(key, (Bundle) value);
                } else if (value instanceof Parcelable) {
                    bundle.putParcelable(key, (Parcelable) value);
                } else {
                    throw new IllegalArgumentException("Unknown type " + value.getClass() + " in Bundle.");
                }
            }
            return this;
        }

从上面代码可以看出,其methodName方法只是将名字”@”传给了它的methodName属性,而call方法则是调用了其外部类 ProviderCaller的call方法。

2.8 ProviderCaller的call方法源码如下:

        public static Bundle call(String auth, Context context, String methodName, String arg, Bundle bundle) {
        Uri uri = Uri.parse("content://" + auth);
        ContentResolver contentResolver = context.getContentResolver();
        return contentResolver.call(uri, methodName, arg, bundle);
    }

可以看到,在该方法中最后实际工作是调用了ContentResolver类的call方法,该方法是Android系统源码中定义的方法,它可以执行在ContentProvider中暴露出来的方法,所有最后实际工作还是在provider中进行的,该流程暂时分析到这里。

2.9 接着进行2.2步的分析

    PatchManager patchManager = PatchManager.getInstance();
            patchManager.injectAll();
            patchManager.checkEnv();
            RuntimeEnv.init();
            PatchManager.fixContext(context);
            isStartUp = true;

从上面代码可以看出,首先创建了一个PatchManager类的实例,然后分别调用了改对象的injectAll方法和checkEnv方法。

2.10 首先分析PatchManager对象的injectAll方法,其源码如下:

        public void injectAll() throws Throwable {
        if (PatchManagerHolder.sInit) {
            throw new IllegalStateException("PatchManager Has been initialized.");
        }
        injectInternal();
        PatchManagerHolder.sInit = true;

    }

该方法中会去调用injectInternal方法,injectInternal方法源码如下:

    private void injectInternal() throws Throwable {
        addPatch(new ActivityManagerPatch());
        addPatch(new PackageManagerPatch());

        if (VirtualCore.getCore().isVAppProcess()) {
            // ## Fuck the MIUI Security
            if (MIUISecurityManagerPatch.needInject()) {
                addPatch(new MIUISecurityManagerPatch());
            }
            // ## End
            addPatch(HCallbackHook.getDefault());
            addPatch(AppInstrumentation.getDefault());
            addPatch(new NotificationManagerPatch());
            addPatch(new LocationManagerPatch());
            addPatch(new WindowManagerPatch());
            addPatch(new ClipBoardPatch());
            addPatch(new MountServicePatch());
            addPatch(new BackupManagerPatch());
            addPatch(new TelephonyPatch());
            addPatch(new PhoneSubInfoPatch());
            addPatch(new PowerManagerPatch());
            addPatch(new TelephonyRegistryPatch());
            addPatch(new AppWidgetManagerPatch());
            addPatch(new AccountManagerPatch());
            addPatch(new DropBoxManagerPatch());
            addPatch(new AudioManagerPatch());
            addPatch(new SearchManagerPatch());

            if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {
                addPatch(new VibratorPatch());
                addPatch(new WifiManagerPatch());
            }
            if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
                addPatch(new UserManagerPatch());
            }

            if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
                addPatch(new DisplayManagerPatch());
            }
            if (Build.VERSION.SDK_INT >= L) {
                addPatch(new InputMethodManagerPatch());
                addPatch(new MmsPatch());
                addPatch(new SessionManagerPatch());
                addPatch(new JobPatch());
                addPatch(new RestrictionPatch());
                addPatch(new CameraPatch());
            }
            if (Build.VERSION.SDK_INT >= KITKAT) {
                addPatch(new AppOpsManagerPatch());
                addPatch(new MediaRouterServicePatch());
            }
            if (Build.VERSION.SDK_INT >= LOLLIPOP_MR1) {
                addPatch(new GraphicsStatsPatch());
            }
        }
    }

在上面代码可以看出,它调用了addPatch方法将许多xxxPatch类添加了进去。addPatch源码如下:

private void addPatch(Injectable injectable) {
        injectableMap.put(injectable.getClass(), injectable);
    }   

从上面代码可以看出,addPatch方法就是讲许多的Injectable对象存放了起来,其中injectableMap的定义如下:

        private Map<Class<?>, Injectable> injectableMap = new HashMap<Class<?>, Injectable>(12);    

因此,addPatch方法就是将Injectable对象存放到了了injectableMap这个Map表中。以ActivityManagerPatch()为例看一下这些xxxPatch对象。

2.11 ActivityManagerPatch类的源码如下:

    @Patch({Hook_StartActivities.class, Hook_StartActivity.class, Hook_StartActivityAsCaller.class,
        Hook_StartActivityAsUser.class, Hook_GetIntentSender.class, Hook_RegisterReceiver.class,
        Hook_GetContentProvider.class, Hook_GetContentProviderExternal.class,

        Hook_GetActivityClassForToken.class, Hook_GetTasks.class, Hook_GetRunningAppProcesses.class,

        Hook_StartService.class, Hook_StopService.class, Hook_StopServiceToken.class, Hook_BindService.class,
        Hook_UnbindService.class, Hook_PeekService.class, Hook_ServiceDoneExecuting.class, Hook_UnbindFinished.class,
        Hook_PublishService.class,

        Hook_HandleIncomingUser.class, Hook_SetServiceForeground.class,

        Hook_BroadcastIntent.class, Hook_GetCallingPackage.class, Hook_GrantUriPermissionFromOwner.class,
        Hook_CheckGrantUriPermission.class, Hook_GetPersistedUriPermissions.class, Hook_KillApplicationProcess.class,
        Hook_ForceStopPackage.class, Hook_AddPackageDependency.class, Hook_UpdateDeviceOwner.class,
        Hook_CrashApplication.class, Hook_GetPackageForToken.class,

        Hook_SetPackageAskScreenCompat.class, Hook_GetPackageAskScreenCompat.class,
        Hook_SetAppLockedVerifying.class,
        Hook_CheckPermission.class,
        Hook_PublishContentProviders.class,
        Hook_GetCurrentUser.class,
        Hook_UnstableProviderDied.class,
        Hook_GetCallingActivity.class,})
    public class ActivityManagerPatch extends PatchObject<IActivityManager, HookObject<IActivityManager>> {

    public static IActivityManager getAMN() {
        return ActivityManagerNative.getDefault();
    }

    @Override
    protected HookObject<IActivityManager> initHookObject() {
        return new HookObject<IActivityManager>(getAMN());
    }

    @Override
    public void inject() throws Throwable {

        Field f_gDefault = ActivityManagerNative.class.getDeclaredField("gDefault");
        if (!f_gDefault.isAccessible()) {
            f_gDefault.setAccessible(true);
        }
        if (f_gDefault.getType() == IActivityManager.class) {
            f_gDefault.set(null, getHookObject().getProxyObject());

        } else if (f_gDefault.getType() == Singleton.class) {

            Singleton gDefault = (Singleton) f_gDefault.get(null);
            Field f_mInstance = Singleton.class.getDeclaredField("mInstance");
            if (!f_mInstance.isAccessible()) {
                f_mInstance.setAccessible(true);
            }
            f_mInstance.set(gDefault, getHookObject().getProxyObject());
        } else {
            // 不会经过这里
            throw new UnsupportedOperationException("Singleton is not visible in AMN.");
        }

        HookBinder<IActivityManager> hookAMBinder = new HookBinder<IActivityManager>() {
            @Override
            protected IBinder queryBaseBinder() {
                return ServiceManager.getService(Context.ACTIVITY_SERVICE);
            }

            @Override
            protected IActivityManager createInterface(IBinder baseBinder) {
                return getHookObject().getProxyObject();
            }
        };
        hookAMBinder.injectService(Context.ACTIVITY_SERVICE);
    }

    @Override
    public boolean isEnvBad() {
        return getAMN() != getHookObject().getProxyObject();
    }
    }

从上面代码可以看出,ActivityManagerPatch这个类大概是用来替换获取AMS的代理对象gDefault的。暂且看到这里,再回到流程2.9中去.

2.12 接下来要执行的就是patchManager.checkEnv()方法,下面看一下checkEnv的源码

    public void checkEnv() throws Throwable {
        for (Injectable injectable : injectableMap.values()) {
            if (injectable.isEnvBad()) {
                injectable.inject();
            }
        }
    }

可以看到,在这个方法中是通过便利injectableMap中的injectable对象,然后执行了每个对象的inject方法。从2.10可以看出,其实这个injectable对象就是前面提到的xxxPatch类的对象。还是以ActivityManagerPatch类为例,首先执行它的isEnvBad方法。

2.13 ActivityManagerPatch类的isEnvBad方法源码如下:

@Override
    public boolean isEnvBad() {
        return getAMN() != getHookObject().getProxyObject();
    }

这个函数的返回值取决于getAMN方法和getHookObject().getProxyObject()方法的返回值,分别来看一下它们的作用。

2.14 getAMN方法的源码如下

    public static IActivityManager getAMN() {
        return ActivityManagerNative.getDefault();
    }

可以看到,这个方法其实返回的就是AMS的代理对象gDefault,再看一下后者的源码。

2.15 getHookObject方法的源码如下

public H getHookObject() {
        return hookObject;
    }

这个方法是ActivityManagerPatch的父类PatchObject中的方法,这里的H就是HookObject类,这里返回的就是一个HookObject类对象。由于PatchObject类是ActivityManagerPatch类的父类,所以在2.10步添加一个新的ActivityManagerPatch类对象是也会同步创建一个PatchObject类对象,看一下PatchObject类对象的源码。

2.16 首先看一下PatchObject类的无参构造方法源码

    public PatchObject() {
        this.hookObject = initHookObject();
        applyHooks();
        afterHookApply(hookObject);
    }

2.17 initHookObject方法的源码如下

    @Override
    protected HookObject<IActivityManager> initHookObject() {
        return new HookObject<IActivityManager>(getAMN());

这个方法在PatchObject类中是一个抽象方法,具体实现是在ActivityManagerPatch中,可以看到其返回一个使用AMS的代理gDefault作为参数的HookObject类对象。继续看一下HookObject类的源码。

2.18 HookObject类的构造方法如下

public HookObject(T baseObject, Class<?>... proxyInterfaces) {
        this(baseObject == null ? null : baseObject.getClass().getClassLoader(), baseObject, proxyInterfaces);
    }

    public HookObject(ClassLoader cl, T baseObject, Class<?>... proxyInterfaces) {
        this.mBaseObject = baseObject;
        if (mBaseObject != null) {
            if (proxyInterfaces == null) {
                proxyInterfaces = baseObject.getClass().getInterfaces();
            }
            mProxyObject = (T) Proxy.newProxyInstance(cl, proxyInterfaces, new HookHandler());
        }
    }

    public HookObject(T baseObject) {
        this(baseObject, (Class<?>[]) null);
    }

可以看到,一共有三个构造方法,但最终会调用到第二个构造方法,在第二个构造方法中,mBaseObject对象就是我们传入的gDefault对象。同时在构造方法末尾创建了一个代理对象mProxyObject。继续2.16步的流程。

2.19 接下来要执行applyHooks()方法,这个方法的源码如下

    protected void applyHooks() {

        if (hookObject == null) {
            return;
        }

        Class<? extends PatchObject> clazz = getClass();
        Patch patch = clazz.getAnnotation(Patch.class);
        int version = Build.VERSION.SDK_INT;
        if (patch != null) {
            Class<? extends Hook>[] hookTypes = patch.value();
            for (Class<? extends Hook> hookType : hookTypes) {
                ApiLimit apiLimit = hookType.getAnnotation(ApiLimit.class);
                boolean needToAddHook = true;
                if (apiLimit != null) {
                    int apiStart = apiLimit.start();
                    int apiEnd = apiLimit.end();
                    boolean highThanStart = apiStart == -1 || version > apiStart;
                    boolean lowThanEnd = apiEnd == -1 || version < apiEnd;
                    if (!highThanStart || !lowThanEnd) {
                        needToAddHook = false;
                    }
                }
                if (needToAddHook) {
                    addHook(hookType);
                }
            }

        }
    }

在上面的步骤中我们已经创建了HookObject对象,所以hookObject对象不为null,接着调用getClass方法获取当前类的类型,即ActivityManagerPatch类,然后获取该类的Patch类型的注解。在2.11步的代码中可以看出,ActivityManagerPatch类注解了patch类型,因此此处的patch不为null。首先是遍历patch注解的所有成员,以Hook_StartActivities类为例,此时hookType为Hook_StartActivities类,然后获取这个类的ApiLimit.class类型的注解。在Hook_StartActivities类中没有看到ApiLimit.class类型的注解,因此apiLImit为null,而needToAddHook因此还是为true,因此需要执行addHook方法。

2.20 addHook方法的源码如下

    private void addHook(Class<? extends Hook> hookType) {
        try {
            Constructor<?> constructor = hookType.getDeclaredConstructors()[0];
            if (!constructor.isAccessible()) {
                constructor.setAccessible(true);
            }
            Hook hook;
            if (constructor.getParameterTypes().length == 0) {
                hook = (Hook) constructor.newInstance();
            } else {
                hook = (Hook) constructor.newInstance(this);
            }
            hookObject.addHook(hook);
        } catch (Throwable e) {
            throw new RuntimeException("Unable to instance Hook : " + hookType + " : " + e.getMessage());
        }
    }

可以看出,在该方法中,主要工作就是创建了一个Hook类型的对象,并将它添加到了hookObject对象中。接下来分析hookObject对象的addHook方法。

2.21 HookObject类的addHook方法源码如下所示

    @Override
    public void addHook(Hook hook) {
        if (hook != null && !TextUtils.isEmpty(hook.getName())) {
            if (internalHookMapping.containsKey(hook.getName())) {
                VLog.w(TAG, "Hook(%s) from class(%s) have been added, can't add again.", hook.getName(),
                        hook.getClass().getName());
            }
            internalHookMapping.put(hook.getName(), hook);
        }
    }

首先,Hook_StartActivities类继承了Hook类,这里传进来的就是一个Hook_StartActivities类对象,很容易判断应该直接执行internalHookMapping.put(hook.getName(), hook)这行代码。internalHookMapping是一个Map对象,存放了需要hook的类,此处将Hook_StartActivities类存放了进去。再回到主流程2.16中。

2.22 接下来要执行afterHookApply(hookObject)代码,它是一个空方法,暂时未执行。

2.23 回到流程2.13中,此时调用getProxyObject()方法,它返回的是2.18步中创建的代理对象mProxyObject。因此isEnvBad方法的返回值为true,再回到2.12中,此时就会执行injectable.inject()

2.24 以ActivityManagerPatch类为例,它的inject源码如下

@Override
    public void inject() throws Throwable {

        Field f_gDefault = ActivityManagerNative.class.getDeclaredField("gDefault");
        if (!f_gDefault.isAccessible()) {
            f_gDefault.setAccessible(true);
        }
        if (f_gDefault.getType() == IActivityManager.class) {
            f_gDefault.set(null, getHookObject().getProxyObject());

        } else if (f_gDefault.getType() == Singleton.class) {

            Singleton gDefault = (Singleton) f_gDefault.get(null);
            Field f_mInstance = Singleton.class.getDeclaredField("mInstance");
            if (!f_mInstance.isAccessible()) {
                f_mInstance.setAccessible(true);
            }
            f_mInstance.set(gDefault, getHookObject().getProxyObject());
        } else {
            // 不会经过这里
            throw new UnsupportedOperationException("Singleton is not visible in AMN.");
        }

从上面代码可以看出,这个函数就是将AMS的代理对象gDefault给替换掉,替换成2.18步创建的代理对象mProxyObject。

2.25 回到流程2.9,此时patchManager.checkEnv()执行完毕,接下来执行RuntimeEnv.init(),init方法的源码如下所示

public static void init() {
        sUIHandler = new Handler(Looper.getMainLooper());
    }   

可以看出,它只是创建了一个以MainLooper为参数的Handler对象。接下来回到流程2.9,接着执行PatchManager.fixContext(context)。

2.26 fixContext方法的源码如下

    public static void fixContext(Context context) {
        while (context instanceof ContextWrapper) {
            context = ((ContextWrapper) context).getBaseContext();
        }
        try {
            Reflect.on(context).set("mPackageManager", null);
            context.getPackageManager();
        } catch (Throwable e) {
            // Ignore
        }
    }

首先,获取了父类ContextWrapper的mBase对象,mBase是一个contextImpl对象,实现了context的功能,,然后将mBase覆盖原有context。接着调用Reflect类的on方法。

2.27 on方法的源码如下所示

    public static Reflect on(Object object) {
        return new Reflect(object);
    }   

在on方法中,直接以传入的context为参数构建了一个新的Reflect对象,接下来调用这个新的Reflect对象的set方法。

2.28 Reflect类的set方法源码如下所示

public Reflect set(String name, Object value) throws ReflectException {
        try {
            Field field = field0(name);
            field.setAccessible(true);
            field.set(object, unwrap(value));
            return this;
        } catch (Exception e) {
            throw new ReflectException(e);
        }
    }

首先调用field0方法。

2.29 field0方法的源码如下所示

    private Field field0(String name) throws ReflectException {
        Class<?> type = type();

        // 先尝试取得公有字段
        try {
            return type.getField(name);
        }

        // 此时尝试非公有字段
        catch (NoSuchFieldException e) {
            do {
                try {
                    return accessible(type.getDeclaredField(name));
                } catch (NoSuchFieldException ignore) {
                }

                type = type.getSuperclass();
            } while (type != null);

            throw new ReflectException(e);
        }
    }

首先通过调用type方法获取上面在创建Reflect对象时所传入的context对象的类类型,然后获取该类的字段mPackageManager。回到2.28中,接下来将context对象的mPackageManager字段置空。回到流程2.26中,接下来调用context.getPackageManager()。

2.30 contextImpl类的getPackageManager方法源码如下

    @Override
            public PackageManager getPackageManager() {
                if (mPackageManager != null) {
                    return mPackageManager;
                }
                IPackageManager pm = ActivityThread.getPackageManager();
                if (pm != null) {
                   return (mPackageManager = new ApplicationPackageManager(this, pm));
                }
                return null;
            }

由于在2.28步中将context对象的mPackageManager字段置空,所以此时会创建一个新的PackageManager类对象,并将其赋给mPackageManager字段。回到2.9,将isStartUp字段置为true,表明已经启动。至此,VirtualCore.getCore().startup(base)执行完毕。

三、在VApp的onCreate方法中注册进程管理器,管理进程变化。

3.1 VApp的onCreate方法源码如下所示

@Override
    public void onCreate() {
        gDefault = this;
        super.onCreate();
        if (VirtualCore.getCore().isMainProcess()) {
            Once.initialise(this);
            LocalProcessManager.registerProcessObserver(new IProcessObserver.Stub() {
                @Override
                public void onProcessCreated(String pkg, String processName) throws RemoteException {
                    VLog.d("VProcess", "Process created: %s -> %s.", pkg, processName);
                }

                @Override
                public void onProcessDied(String pkg, String processName) throws RemoteException {
                    VLog.d("VProcess", "Process died: %s -> %s.", pkg, processName);
                }
            });
        }
    }

首先,将当前类对象传给gDefault,然后调用VirtualCore类的isMainProcess方法判断当前类是否为主进程,如果是的话再进行进一步处理。先调用Once类进行初始化,然后调用LocalProcessManager类的registerProcessObserver方法,

3.2 registerProcessObserver方法的源码如下所示

    public static void registerProcessObserver(IProcessObserver observer) {
        try {
            getService().registerProcessObserver(observer);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

首先调用getService方法获取一个service对象,然后进一步调用了这个service对象的registerProcessObserver方法。

3.3 首先看一下gerService方法的源码

private static IProcessManager service; 
    private static IProcessManager getService() {
        if (service == null) {
            synchronized (LocalProcessManager.class) {
                if (service == null) {
                    IBinder binder = ServiceManagerNative.getService(ServiceManagerNative.PROCESS_MANAGER);
                    service = IProcessManager.Stub.asInterface(binder);
                }
            }
        }
        if (service == null) {
            throw new RuntimeException("Unable to attach ProcessManager");
        }
        return service;
    }

可以看到方法最后是调用了ServiceManagerNative的getService方法获取了一个binder对象,然后将此binder对象包装后传给service。可以看到,此处代码与2.4处相同,执行到最后会调用contentResolver的call方法来获取Bundle对象,然后从该Bundle对象中取出保存在对象里面的Binder对象。

至此,VA的Application启动过程就分析完成。

VirtualApp技术黑产利用研究报告 一、 前言 VirtualApp(以下称VA)是一个App虚拟化引擎(简称VA)。VirtualApp创建了一个虚拟空间,你可以在虚拟空间内任意的安装、启动和卸载APK,这一切都与外部隔离,如同一个沙盒。运行在VA中的APK无需在Android系统中安装即可运行,也就是我们熟知的多开应用。 VA免安装运行APK的特性使得VA内应用与VA相比具有不同的应用特征,这使得VA可用于免杀。此外,VA对被多... 阅读详情

相关推荐

【VirtualAPP 双开系列08】如何实现多开 - UID

目录: 1. Android UID 介绍 2.多开基本原理 3. 常用的多开方案 4. VirtualAPP 如何实现多开 1. Android UID 介绍 Android 系统中修改了 Linux 的 UID 的含义。由于 Android 是单用户系统,不需要支持多用户登陆,因此传统的 UID 系统就失去了原来的意义。Android 的开发者巧妙地修改了 UID 的含义:每个 APP 对应一个 UID—用 UID 对应用程序进行管理。 Android 中查看 UID 的方法: ..

u014294681的博客 1万+

X Video Extension Protocol Description

https://www.x.org/releases/X11R7.7/doc/videoproto/xv-protocol-v2.txt The Xv extension does the following: -- lists available video adaptors -- identifies the number of ports each adaptor supports -- describes what drawable formats each adaptor su

叶落随风 7万+

MIA文献阅读 —— 深度学习在医学图像分析中的最新进展及临床应用【2024】_再现性;深度学习

coderVAEVAEVAE) (Kingma威林,2014)在概率性的工作方式学习观测数据之间的映射空间x∈Rmx∈R^mx∈Rm 和潜在的空间z∈Rnmnz∈Rn(m>>n)。作为一个潜变量模型,VAEVAEVAE制定这个问题作为观察样品的最大化对数似logpxlog∫px∣zpzdzpx∣zp (x | z)p(x∣z)可以很容易地使用神经网络建模,和pzp (z)

2401_84181704的博客 1242

iphonex系列机型判断

iphonex xs xr xsmax 判断 // iphonex xs xr xsmax 判断 export function isIphonX() { // iPhone X、iPhone XS var isIPhoneX = /iphone/gi.test(window.navigator.userAgent) && window.devicePixelRat...

齐枫的博客 3804

AlphaFold2算法详解

正文:https://www.nature.com/articles/s41586-021-03819-2 补充文件:https://static-content.springer.com/esm/art%3A10.1038%2Fs41586-021-03819-2/MediaObjects/41586_2021_3819_MOESM1_ESM.pdfhttps://static-content.springer.com/esm/art%3A10.1038%2Fs41586-021-03819-2/Med

Daisy4的博客 1万+

YOLOv8改进策略【独家融合改进】| StarNet + 小目标检测头,加强跨尺度的上下文特征融合,提高小目标检测能力

P3/8 - small检测头原始模型中的P3/8特征层对应的检测头主要用于检测相对较小的目标。其特征图大小相对较大,空间分辨率较高。适合检测尺寸大概在8x8到32x32像素左右的目标。P4/16 - medium检测头这个检测头对应的P4/16特征层经过了更多的下采样操作,相比P3/8特征图空间分辨率降低,但通道数增加,特征更抽象且有语义信息。它主要用于检测中等大小的目标,尺寸范围大概在32x32到64x64像素左右。P5/32 - large检测头P5/32。

Limiiiing的博客 635

【Android 插件化】VirtualApp 接入 ( 在 VirtualApp 工程下创建 Module | 添加依赖 | 启动 VirtualApp 插件引擎 )

一、在 VirtualApp 工程下创建 Module、 二、添加依赖、 三、启动 VirtualApp 插件引擎、 四、博客源码、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 2141

基于深度学习的活体人脸检测系统:YOLOv5、YOLOv6、YOLOv7、YOLOv8实现与UI界面设计

本博客详细介绍了如何基于YOLOv5、YOLOv6、YOLOv7、YOLOv8模型实现一个完整的活体人脸检测系统。系统包括数据集准备、模型训练、UI界面设计以及实时检测功能的实现。通过YOLO系列模型的应用,我们能够高效、准确地进行人脸检测和活体验证。未来,随着YOLOv9和其他新一代目标检测模型的出现,我们可以进一步提高检测精度和速度。此外,集成更多的传感器数据(如深度传感器、红外线传感器)可以使活体检测系统更加安全和稳定。

YOLO 540

Android 双开沙箱 VirtualApp 源码分析(三)App 启动

Android 双开沙箱 VirtualApp 源码分析(三)App 启动 在这之前,我们还是要先了解一下 VA Client Framework 和 VAService 之间的通讯方式 VAService 与通讯 VAService 首先,VAService 是指 VA 仿造 Android 原生 framework 层 Service 实现的一套副本,举例有 VActivityManagerS...

CoderAldrich的专栏 797

【Unity 实用工具篇】 | Unity网格破碎插件 unity-fracture

前面写过一篇Unity中物体破碎效果的插件:【Unity 实用工具篇】 | Unity物体破碎效果 OpenFracture 本文介绍一个另外的工具unity-fracture,也可以实现网格破碎效果,且功能与OpenFracture略有差异。

努力前行,总会成为自己心中的那道光 1万+

RK3588开发笔记-MIPI-CSI接口视频解码芯片XS9922B调试

Rockchip RK3588 是一款高性能的处理器,支持多种摄像头接口,包括 MIPI(Mobile Industry Processor Interface)接口。MIPI是一种用于移动设备和嵌入式系统中的摄像头、显示器和其他传感器设备的串行接口标准。MIPI是MIPI联盟发起的为移动应用处理器制定的开放标准。CSI-2(Camera Serial Interface 2)是MIPI 联盟定义的另一种高速接口,主要用于摄像模组和处理器之间的连接。

flypig has a dream! 6861

VirtualApp启动加速指南:5步优化工具实操教程

你是否还在为VirtualApp启动缓慢而烦恼?每次打开虚拟应用都要等待漫长的加载时间?本文将通过5个简单步骤,带你掌握VirtualApp启动优化工具的使用方法,让你的虚拟应用启动速度提升30%以上。读完本文后,你将能够:配置启动参数、优化资源加载、使用Native Hook加速关键路径、调整进程管理策略以及验证优化效果。 ## 1. 配置启动参数 VirtualApp的启动性能很大程度上取...

gitblog_00446的博客 1067

AUTOSAR 导入DBC ISOLAR工程配置

ISOLAR工程配置-dbc导入

汽车电子电气 2406

VirtualApp 悬浮窗权限适配实战:从 Android 6 到 14 不闪退的 7 个关键点

VirtualApp 是一款开源的 Android 沙盒多开框架,通过 Hook 系统服务让应用分身并行运行;当你在它之上叠加悬浮窗功能时,最难缠的不是业务逻辑,而是 Android 6 到 Android 14 之间不断收紧的悬浮窗权限。这篇文章用一次真实的上线事故做引子,把 SYSTEM_ALERT_WINDOW 的申请、窗口类型选择、后台限制和多进程同步一次讲透。 ## 一、一次真实事故:

gitblog_00918的博客 731

深度学习-155-RAG技术之嵌入模型Embedding怎么选

Embedding技术在大型语言模型中发挥着至关重要的作用。它将文本转化为向量,使模型能够理解和处理复杂的语言信息。通过选择合适的Embedding方法和优化文本匹配策略,我们可以在搜索引擎、问答系统、推荐系统等领域实现高效且精准的文本匹配。

qq_20466211的博客 405

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

VirtualApp实战指南:从集成到部署的完整流程

本文全面介绍了VirtualApp Android沙盒技术的完整集成和使用流程。从SDK集成步骤详解开始,涵盖了VAConfig.gradle配置参数解析、Application初始化与进程管理机制,到应用安装与启动API的详细使用指南。文章通过丰富的代码示例、流程图和配置表格,深入讲解了VirtualApp的核心功能,包括多进程架构设计、IO重定向、权限控制、多用户环境支持等关键技术点,为开发者...

gitblog_00399的博客 1304

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

从一次诡异崩溃说起:VirtualApp 悬浮窗权限的十年适配之路

导语:你在真机上测得好好的悬浮窗功能,上线后却收到一屏"无法添加窗口"的崩溃日志;同一个包名,Android 9 上正常,Android 12 上却毫无反应。作为一款主打应用多开与沙盒运行的引擎,VirtualApp 几乎把每个 Android 版本的权限规则都踩了一遍。这篇文章不堆概念,只讲坑——从崩溃现场出发,带你逐层拆解悬浮窗权限在 Android 6 到 14 之间的真实行为差异,并给出可

gitblog_00708的博客 807

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider

Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider Provider 注册 回顾前面,Activity 启动的时候会检查 Application 是否初始化,会调用 bindApplication,里面执行了安装 Provider 的方法: private void installContentProviders(Context app, List<...

CoderAldrich的专栏 616

悬浮窗权限从Android 6适配到14:VirtualApp避坑实战与一劳永逸方案

VirtualApp 是一个开源的 Android 沙盒引擎,主打应用多开与双开。做多开,几乎绕不开悬浮窗权限适配——从 Android 6 一路升到 14,这套权限规则改了好几轮,稍不留神,悬浮窗就罢工。本文从一个真实故障说起,带你把权限检查、窗口类型、前台服务这些适配要点一次理清,文末附可直接抄的代码。 ## 一个真实事故:手机升完级,悬浮窗不见了 🕵️ 先讲个我同事的遭遇。他把测试机从

gitblog_00062的博客 885

VirtualApp深度解析:Android虚拟化引擎的完整技术架构与实战指南

VirtualApp作为Android平台上领先的虚拟化引擎解决方案,为开发者提供了强大的应用沙盒环境。这款创新的技术框架支持从Android 5.0到17.0的全版本兼容,实现了应用多开、数据隔离、免Root Hook等核心功能。通过深入分析VirtualApp的技术架构和实现原理,本文将为您揭示这一虚拟化引擎的完整技术体系。 ## 📊 VirtualApp技术架构全景解析 Virtual

gitblog_00970的博客 513

终极指南:如何快速掌握VirtualApp安卓沙盒开源项目

VirtualApp是一款革命性的Android沙盒开源项目,它让开发者能够在同一设备上运行多个应用实例,实现应用多开、游戏合集、移动办公安全等多种创新场景。这个强大的安卓沙盒技术通过三个层次的技术实现应用虚拟化,为Android开发者提供了前所未有的灵活性和控制能力。 ## 🚀 VirtualApp的核心价值与应用场景 VirtualApp不仅仅是一个简单的应用多开工具,它是一个完整的An

gitblog_01030的博客 399

VirtualApp沙盒 基本原理

转自:http://rk700.github.io/2017/03/15/virtualapp-basic/ VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。通过阅读源码及动态调试,基本了解了其运行原理,在此记录。 本质 Android应用隔离是基于Linux系统的多用户机制实现的,即每个应用在安装时被分配了不同的Linux用户uid/gid。而在VirtualApp中,client应用(通过...

gghhm的博客 1655

VirtualApp实战指南:5分钟构建安卓应用沙盒环境

还在为手机应用多开需求而烦恼?想在同一设备上运行多个相同应用却受限于系统约束?VirtualApp作为Android平台上的轻量级沙盒解决方案,让你无需Root权限即可实现应用双开、多开功能。本文将带你快速掌握VirtualApp的核心使用方法,在5分钟内搭建完整的应用沙盒环境。 通过本文你将学会: - 理解VirtualApp沙盒隔离技术原理 - 掌握环境搭建与配置的核心步骤 - 实现虚拟应用

gitblog_00284的博客 1119

深度解析VirtualApp安卓沙盒技术:三步掌握Android应用虚拟化核心原理

VirtualApp是一款运行在Android系统上的沙盒产品,可以理解为轻量级的"Android虚拟机"。这款强大的安卓沙盒技术让开发者能够在同一设备上运行多个应用实例,实现应用多开、游戏合集、移动办公安全等多种应用场景。通过本文,您将深入了解VirtualApp的技术架构、实现原理和实战应用。 ## 技术挑战:传统Android应用隔离的局限性 在Android生态系统中,应用沙盒化一直是

gitblog_00693的博客 399

VirtualApp 悬浮窗权限跨版本适配全攻略:一次搞定 Android 6 到 14 的兼容难题

深夜十一点,运营同事在群里连发三条消息:"双开的应用挂后台两小时,悬浮球不见了""用户反馈 Android 14 上悬浮窗显示不了""还有台 Android 8 的机器,悬浮窗弹出来就闪退"。这三个问题指向同一个技术债——**悬浮窗权限在不同 Android 版本下的管理规则完全不同**,而 VirtualApp 作为一款 Android 虚拟引擎(通过 Hook 系统服务为应用提供隔离运行环境,

gitblog_00378的博客 180

Daily Paper Reader部署指南:从GitHub Pages到绑定自定义域名,打造私人论文站

Daily Paper Reader 是一个开源的每日 arXiv / OpenReview 论文推荐与 AI 阅读平台,基于 GitHub Actions 自动更新、GitHub Pages 零服务器部署。这份部署指南带你完成 Fork 仓库、开启 Actions 与 Pages、配置订阅、绑定自定义域名的完整流程,约 5 分钟即可上线属于你自己的私人论文站。 ## 📌 Daily Pape

gitblog_00908的博客 214
上一篇: ELF文件格式解析
下一篇: Android逆向教程收藏
flyeagle317
博客等级 码龄14年 26粉丝 30原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值