virtualapp启动流程源码分析
virtualapp启动流程分析
1. 首先是启动本身,执行Vpp 的attachBaseContext
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
mPreferences = base.getSharedPreferences("va", Context.MODE_MULTI_PROCESS);
VASettings.ENABLE_IO_REDIRECT = true;
VASettings.ENABLE_INNER_SHORTCUT = false;
try {
VirtualCore.get().startup(base);
} catch (Throwable e) {
e.printStackTrace();
}
}
在这里执行了VirtualCore.get().startup(base);
public void startup(Context context) throws Throwable {
if (!isStartUp) {
......
detectProcessType();
InvocationStubManager invocationStubManager = InvocationStubManager.getInstance();
invocationStubManager.init();
invocationStubManager.injectAll();
ContextFixer.fixContext(context);
isStartUp = true;
......
}
}
这里最主要的是实例化了invocationStubManager ,并开启注入(injectAll),
public void init() throws Throwable {
if (isInit()) {
throw new IllegalStateException("InvocationStubManager Has been initialized.");
}
injectInternal();
sInit = true;
}
private void injectInternal() throws Throwable {
if (VirtualCore.get().isMainProcess()) {
return;
}
......
}
由于当前我们是主进程,所以这里注入是啥也没干
2. 然后进入APP 的主Activity(SplashActivity)
protected void onCreate(Bundle savedInstanceState) {
@SuppressWarnings("unused")
boolean enterGuide = !Once.beenDone(Once.THIS_APP_INSTALL, VCommends.TAG_NEW_VERSION);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
VUiKit.defer().when(() -> {
if (!Once.beenDone("collect_flurry")) {
FlurryROMCollector.startCollect();
Once.markDone("collect_flurry");
}
long time = System.currentTimeMillis();
doActionInThread();
time = System.currentTimeMillis() - time;
long delta = 3000L - time;
if (delta > 0) {
VUiKit.sleep(delta);
}
}).done((res) -> {
HomeActivity.goHome(this);
finish();
});
}
private void doActionInThread() {
if (!VirtualCore.get().isEngineLaunched()) {
VirtualCore.get().waitForEngine();
}
}
在这里异步执行了doActionInThread,也就是执行了waitForEngine,这一步很重要,在这里会开启另一个进程
public void waitForEngine() {
ServiceManagerNative.ensureServerStarted();
}
ServiceManagerNative.java
public static String SERVICE_CP_AUTH = "virtual.service.BinderProvider";
......
public static void ensureServerStarted() {
new ProviderCall.Builder(VirtualCore.get().getContext(), SERVICE_CP_AUTH).methodName("ensure_created").call();
}
ProviderCall.java
public static Bundle call(String authority, Context context, String method, String arg, Bundle bundle) {
Uri uri = Uri.parse("content://" + authority);
return ContentProviderCompat.call(context, uri, method, arg, bundle);
}
最终会构造出一个uri: virtual.service.BinderProvider, 并启动这个Provider
ContentProviderCompat.java
public static Bundle call(Context context, Uri uri, String method, String arg, Bundle extras) {
if (VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return context.getContentResolver().call(uri, method, arg, extras);
}
ContentProviderClient client = crazyAcquireContentProvider(context, uri);
Bundle res = null;
try {
res = client.call(method, arg, extras);
} catch (RemoteException e) {
e.printStackTrace();
} finally {
releaseQuietly(client);
}
return res;
}
......
private static ContentProviderClient acquireContentProviderClient(Context context, Uri uri) {
if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return context.getContentResolver().acquireUnstableContentProviderClient(uri);
}
return context.getContentResolver().acquireContentProviderClient(uri);
}
也就是最终使用context.getContentResolver().acquireContentProviderClient 获得一个client引用,并执行他的client.call(method, arg, extras);,经上面分析method的值为ensure_created,
这里的目的就是为了唤醒这个BinderProvider
查看它在AndroidManifest.xml中的配置
<provider
android:name="com.lody.virtual.server.BinderProvider"
android:authorities="${applicationId}.virtual.service.BinderProvider"
android:exported="false"
android:process="@string/engine_process_name" />
<string name="engine_process_name">:x</string>
那么它是存在另一个进程中的,那么根据命名规则,该进程名会是io.virtualapp:x
也就是说现在有两个进程了
主进程
io.virtualapp
服务进程
io.virtualapp:x
2. 服务进程启动流程
由于provider的启动,那么流程又会走到VApp中,在这个进程中走attachBaseContext的VirtualCore.get().startup(base);
然后同样的走
InvocationStubManager invocationStubManager = InvocationStubManager.getInstance();
invocationStubManager.init();
invocationStubManager.injectAll();
InvocationStubManager.java
private void injectInternal() throws Throwable {
....
if (VirtualCore.get().isServerProcess()) {
addInjector(new ActivityManagerStub());
addInjector(new PackageManagerStub());
return;
....
}
void injectAll() throws Throwable {
for (IInjector injector : mInjectors.values()) {
injector.inject();
}
// XXX: Lazy inject the Instrumentation,
addInjector(AppInstrumentation.getDefault());
}
在这里会hook掉ActivityManager 和PackageManager
先查看PackageManager 的hooker PackageManagerStub
@Inject(MethodProxies.class)
public final class PackageManagerStub extends MethodInvocationProxy<MethodInvocationStub<IInterface>> {
public PackageManagerStub() {
super(new MethodInvocationStub<>(ActivityThread.sPackageManager.get()));
}
@Override
protected void onBindMethods() {
super.onBindMethods();
addMethodProxy(new ResultStaticMethodProxy("addPermissionAsync", true));
addMethodProxy(new ResultStaticMethodProxy("addPermission", true));
addMethodProxy(new ResultStaticMethodProxy("performDexOpt", true));
addMethodProxy(new ResultStaticMethodProxy("performDexOptIfNeeded", false));
addMethodProxy(new ResultStaticMethodProxy("performDexOptSecondary", true));
addMethodProxy(new ResultStaticMethodProxy("addOnPermissionsChangeListener", 0));
addMethodProxy(new ResultStaticMethodProxy("removeOnPermissionsChangeListener", 0));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
addMethodProxy(new ResultStaticMethodProxy("checkPackageStartable", 0));
}
if (BuildCompat.isOreo()) {
addMethodProxy(new ResultStaticMethodProxy("notifyDexLoad", 0));
addMethodProxy(new ResultStaticMethodProxy("notifyPackageUse", 0));
addMethodProxy(new ResultStaticMethodProxy("setInstantAppCookie", false));
addMethodProxy(new ResultStaticMethodProxy("isInstantApp", false));
}
}
@Override
public void inject() throws Throwable {
final IInterface hookedPM = getInvocationStub().getProxyInterface();
ActivityThread.sPackageManager.set(hookedPM);
BinderInvocationStub pmHookBinder = new BinderInvocationStub(getInvocationStub().getBaseInterface());
pmHookBinder.copyMethodProxies(getInvocationStub());
pmHookBinder.replaceService("package");
}
@Override
public boolean isEnvBad() {
return getInvocationStub().getProxyInterface() != ActivityThread.sPackageManager.get();
}
}
这里就是创建了一个PackageManager 的一个代理, 通过Java动态代理的方式,并通过反射写回到ActivityThread.sPackageManager中,实现这个对象的代理,走到我们自己对象中
然后走到BinderProvider的onCreate中
@Override
public boolean onCreate() {
Context context = getContext();
DaemonService.startup(context);
if (!VirtualCore.get().isStartup()) {
return true;
}
VPackageManagerService.systemReady();
IPCBus.register(IPackageManager.class, VPackageManagerService.get());
VActivityManagerService.systemReady(context);
IPCBus.register(IActivityManager.class, VActivityManagerService.get());
IPCBus.register(IUserManager.class, VUserManagerService.get());
VAppManagerService.systemReady();
IPCBus.register(IAppManager.class, VAppManagerService.get());
BroadcastSystem.attach(VActivityManagerService.get(), VAppManagerService.get());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
IPCBus.register(IJobService.class, VJobSchedulerService.get());
}
VNotificationManagerService.systemReady(context);
IPCBus.register(INotificationManager.class, VNotificationManagerService.get());
VAppManagerService.get().scanApps();
VAccountManagerService.systemReady();
IPCBus.register(IAccountManager.class, VAccountManagerService.get());
IPCBus.register(IVirtualStorageService.class, VirtualStorageService.get());
IPCBus.register(IDeviceInfoManager.class, VDeviceManagerService.get());
IPCBus.register(IVirtualLocationManager.class, VirtualLocationService.get());
return true;
}
BinderProvideronCreate方法,在其中启动后台的DaemonService,保持:x进程不被系统杀死。然后初始化各个模拟的系统服务,如VPackageManagerService、VUserManagerService、VActivityManagerService、VAppManagerService、VNotificationManagerService、VAccountManagerService等,并注册到ServiceCache中
最终通过 IServiceFetcher.aidl 实现rpc通信,实现客户端进程来服务端访问数据
IServiceFetcher.Stub 的实现在BinderProvider.java 中
private class ServiceFetcher extends IServiceFetcher.Stub {
@Override
public IBinder getService(String name) throws RemoteException {
if (name != null) {
return ServiceCache.getService(name);
}
return null;
}
@Override
public void addService(String name, IBinder service) throws RemoteException {
if (name != null && service != null) {
ServiceCache.addService(name, service);
}
}
@Override
public void removeService(String name) throws RemoteException {
if (name != null) {
ServiceCache.removeService(name);
}
}
}
从这里可以看出, 当调用getService 时,实际时从ServiceCache中访问之前注册的service
那么客户端如何获得Fetcher呢
BinderProvider.java
@Override
public Bundle call(String method, String arg, Bundle extras) {
if ("@".equals(method)) {
Bundle bundle = new Bundle();
BundleCompat.putBinder(bundle, "_VA_|_binder_", mServiceFetcher);
return bundle;
}
if ("register".equals(method)) {
}
return null;
}
当客户段通过BinderProvider 并调用方法名为"@"时,会返回mServiceFetcher,并通过
sFetcher = IServiceFetcher.Stub.asInterface(binder); 创建客户端的interface,具体可以参考
ServiceManagerNative.java 的以下方法
private static IServiceFetcher getServiceFetcher() {
if (sFetcher == null || !sFetcher.asBinder().isBinderAlive()) {
synchronized (ServiceManagerNative.class) {
Context context = VirtualCore.get().getContext();
Bundle response = new ProviderCall.Builder(context, SERVICE_CP_AUTH).methodName("@").call();
if (response != null) {
IBinder binder = BundleCompat.getBinder(response, "_VA_|_binder_");
linkBinderDied(binder);
sFetcher = IServiceFetcher.Stub.asInterface(binder);
}
}
}
return sFetcher;
}
客户段通过这个Fetcher来访问服务端的各个服务,而各个服务都是封装成了TransformBinder 对象,TransformBinder 继承于Binder, 可通过RPC进行通信
至此virtual app 启动完毕
virtualapp启动流程源码分析的更多相关文章
- Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)
上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...
- Spark(四十九):Spark On YARN启动流程源码分析(一)
引导: 该篇章主要讲解执行spark-submit.sh提交到将任务提交给Yarn阶段代码分析. spark-submit的入口函数 一般提交一个spark作业的方式采用spark-submit来提交 ...
- springboot的启动流程源码分析
.测试项目,随便一个简单的springboot项目即可: 直接debug调试: 可见,分2步,第一步是创建SpringApplication对象,第二步是调用run方法: 1.SpringApplic ...
- Spring Boot的自动配置原理及启动流程源码分析
概述 Spring Boot 应用目前应该是 Java 中用得最多的框架了吧.其中 Spring Boot 最具特点之一就是自动配置,基于Spring Boot 的自动配置,我们可以很快集成某个模块, ...
- SpringBoot启动流程源码分析
前言 SpringBoot项目的启动流程是很多面试官面试中高级Java程序员喜欢问的问题.这个问题的答案涉及到了SpringBoot工程中的源码,也许我们之前看过别的大牛写过的有关SpringBoot ...
- SpringBoot 源码解析 (二)----- Spring Boot精髓:启动流程源码分析
本文从源代码的角度来看看Spring Boot的启动过程到底是怎么样的,为何以往纷繁复杂的配置到如今可以这么简便. 入口类 @SpringBootApplication public class He ...
- Spark On YARN启动流程源码分析(一)
本文主要参考: a. https://www.cnblogs.com/yy3b2007com/p/10934090.html 0. 说明 a. 关于spark源码会不定期的更新与补充 b. 对于spa ...
- 面试必备:Android Activity启动流程源码分析
最近大致分析了一把 Activity 启动的流程,趁着今天精神状态好,把之前记录的写成文章. 开门见山,我们直接点进去看 Activity 的 startActivity , 最终,我们都会走到 st ...
- SpringBoot一站式启动流程源码分析
一.前言 由上篇文章我们得知,SpringBoot启动时,就是有很简单的一行代码.那我们可以很清楚的看到这行代码的主角便是SpringApplication了,本文我们就来聊一聊这货,来探寻Sprin ...
- Dubbo学习笔记10:Dubbo服务消费方启动流程源码分析
同理我们看下服务消费端启动流程时序图: 在<Dubbo整体架构分析>一文中,我们提到服务消费方需要使用ReferenceConfig API来消费服务,具体是调用代码(1)get()方法来 ...
随机推荐
- [转帖]【OS】OSWbb(OSWatcher Black Box)的简介和使用
http://blog.itpub.net/26736162/viewspace-2142613/ OSWatcher Black Box, 简称OSW,是提供的一个小但是非常有用的工具,它通过调用O ...
- [转帖]集群监控之 —— ipmi操作指南
https://www.cnblogs.com/gaoyuechen/p/8506930.html 这两天,配置了一堆500来个节点的大型集群,被ipmi的问题困扰了一天半,到下午16:40,终于解决 ...
- 【转帖】SRE 高延迟问题的罪魁祸首 System.gc()
https://www.infoq.cn/article/lXTRgYb9ecVBu*72fT7O jstact -gccause pid 3000 30 01 案例一: 某日,支付平台的开发人员找到 ...
- [转帖]Linux系统中的Page cache和Buffer cache
Free命令显示内存 首先,我们来了解下内存的使用情况: Mem:表示物理内存统计 total:表示物理内存总量(total = used + free) used:表示总计分配给缓存(包含buffe ...
- CS5280H 无网络安装KVM虚拟机的过程
背景 信创海光机器 想进行虚拟化 自带了银河麒麟V10 SP1的操作系统. 但是没有安装virt-manager等工具 会议室里面的网口又都坏了. 所以准备挑战一下无网络安装KVM. 过程1 第一步. ...
- vCenter 6.7 使用Grafana监控失败的处理
背景 国庆处理的vCenter监控. 老的vCenter6.0的平台很正常. 但是新的vCenter 6.7 就经常出现断连的情况. 花费了快一个多小时才搞定, 这里记录一下. 问题现象 vCente ...
- .Net Core 3.1浏览器后端服务(二) Web API项目分层
一.前言 分层开发的思想在计算机领域中至关重要,从操作系统到软件设计,分层思想无处不在. 在搭建项目的分层结构前,先简单了解下分层的优缺点.如下图,分为(呈现层.业务层.服务层.数据层) 分层的优点: ...
- chaincode中使用第三方库
本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处. 在fabric的chaincode开发时,有时候需要用到第三方库提供的功能.这 ...
- # 重要-即时通讯IM开源项目OpenIM关于版本管理及v2.3.0发布计划
越来越多的客户把OpenIM用到了生产环境,由于新特性持续迭代和bug修复,会涉及到后续的升级方案,为了让大家后续从容应对,本文重点总结OpenIM对未来版本管理的思路和方案.同时,官网对于文档进行了 ...
- Go语言的100个错误使用场景(一)|代码和项目组织
目录 前言 1. Go: Simple to learn but hard to master 1.1 Go 语言概述 1.2 简单不等于容易 1.3 使用 Go 的100个错误 2. Code an ...