应用启动源码分析

在HomeActvity中的OnCreate方法会调用initLaunchpad

    private void initLaunchpad() {
mLauncherView.setHasFixedSize(true);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, OrientationHelper.VERTICAL);
mLauncherView.setLayoutManager(layoutManager);
mLaunchpadAdapter = new LaunchpadAdapter(this);
SmartRecyclerAdapter wrap = new SmartRecyclerAdapter(mLaunchpadAdapter);
View footer = new View(this);
footer.setLayoutParams(new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, VUiKit.dpToPx(this, 60)));
wrap.setFooterView(footer);
mLauncherView.setAdapter(wrap);
mLauncherView.addItemDecoration(new ItemOffsetDecoration(this, R.dimen.desktop_divider));
ItemTouchHelper touchHelper = new ItemTouchHelper(new LauncherTouchCallback());
touchHelper.attachToRecyclerView(mLauncherView);
mLaunchpadAdapter.setAppClickListener((pos, data) -> {
if (!data.isLoading()) {
if (data instanceof AddAppButton) {
onAddAppButtonClick();
}
mLaunchpadAdapter.notifyItemChanged(pos);
mPresenter.launchApp(data);
}
});
}

当点击图标时会走到mPresenter.launchApp(data)

HomePresenterImpl.java

    public void launchApp(AppData data) {
try {
if (data instanceof PackageAppData) {
PackageAppData appData = (PackageAppData) data;
appData.isFirstOpen = false;
LoadingActivity.launch(mActivity, appData.packageName, 0);
} else if (data instanceof MultiplePackageAppData) {
MultiplePackageAppData multipleData = (MultiplePackageAppData) data;
multipleData.isFirstOpen = false;
LoadingActivity.launch(mActivity, multipleData.appInfo.packageName, ((MultiplePackageAppData) data).userId);
}
} catch (Throwable e) {
e.printStackTrace();
}
}

一般情况时PackageAppData,那么LoadingActivity.launch(mActivity, appData.packageName, 0);

这里其实就是进入LoadingActivity,在它的OnCreate方法中会执行

 VActivityManager.get().startActivity(intent, userId);

此时userId是0(由上面的代码分析可知).intent里由packageName

VActivityManager.java

    public int startActivity(Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode, int userId) {
try {
return getService().startActivity(intent, info, resultTo, options, resultWho, requestCode, userId);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}

那么RPC调用,最终会走到VActivityManagerService.java

    @Override
public int startActivity(Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode, int userId) {
synchronized (this) {
return mMainStack.startActivityLocked(userId, intent, info, resultTo, options, resultWho, requestCode);
}
}

假设我们是第一次进这个APP,最终会执行到MainStack的startActivityInNewTaskLocked方法

    private void startActivityInNewTaskLocked(int userId, Intent intent, ActivityInfo info, Bundle options) {
Intent destIntent = startActivityProcess(userId, null, intent, info);
if (destIntent != null) {
destIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
destIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
destIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// noinspection deprecation
destIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
} else {
destIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
} if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
VirtualCore.get().getContext().startActivity(destIntent, options);
} else {
VirtualCore.get().getContext().startActivity(destIntent);
}
}
}

这里有一个关键方法startActivityProcess,通过startActivityProcess来获得一个目标Intent,然后再使用getContext().startActivity(destIntent);来打开这个Activity

    private Intent startActivityProcess(int userId, ActivityRecord sourceRecord, Intent intent, ActivityInfo info) {
intent = new Intent(intent);
ProcessRecord targetApp = mService.startProcessIfNeedLocked(info.processName, userId, info.packageName);
if (targetApp == null) {
return null;
}
Intent targetIntent = new Intent();
targetIntent.setClassName(VirtualCore.get().getHostPkg(), fetchStubActivity(targetApp.vpid, info));
ComponentName component = intent.getComponent();
if (component == null) {
component = ComponentUtils.toComponentName(info);
}
targetIntent.setType(component.flattenToString());
StubActivityRecord saveInstance = new StubActivityRecord(intent, info,
sourceRecord != null ? sourceRecord.component : null, userId);
saveInstance.saveToIntent(targetIntent);
return targetIntent;
}

可以看到该方法通过fetchStubActivity(targetApp.vpid, info) 来获得Activity的类名

    private String fetchStubActivity(int vpid, ActivityInfo targetInfo) {

        boolean isFloating = false;
boolean isTranslucent = false;
boolean showWallpaper = false;
try {
int[] R_Styleable_Window = R_Hide.styleable.Window.get();
int R_Styleable_Window_windowIsTranslucent = R_Hide.styleable.Window_windowIsTranslucent.get();
int R_Styleable_Window_windowIsFloating = R_Hide.styleable.Window_windowIsFloating.get();
int R_Styleable_Window_windowShowWallpaper = R_Hide.styleable.Window_windowShowWallpaper.get(); AttributeCache.Entry ent = AttributeCache.instance().get(targetInfo.packageName, targetInfo.theme,
R_Styleable_Window);
if (ent != null && ent.array != null) {
showWallpaper = ent.array.getBoolean(R_Styleable_Window_windowShowWallpaper, false);
isTranslucent = ent.array.getBoolean(R_Styleable_Window_windowIsTranslucent, false);
isFloating = ent.array.getBoolean(R_Styleable_Window_windowIsFloating, false);
}else{
Resources resources=VirtualCore.get().getResources(targetInfo.packageName);
if(resources!=null) {
TypedArray typedArray = resources.newTheme().obtainStyledAttributes(targetInfo.theme, R_Styleable_Window);
if(typedArray!=null){
showWallpaper = typedArray.getBoolean(R_Styleable_Window_windowShowWallpaper, false);
isTranslucent = typedArray.getBoolean(R_Styleable_Window_windowIsTranslucent, false);
isFloating = typedArray.getBoolean(R_Styleable_Window_windowIsFloating, false);
}
}
}
} catch (Throwable e) {
e.printStackTrace();
} boolean isDialogStyle = isFloating || isTranslucent || showWallpaper;
if (isDialogStyle) {
return VASettings.getStubDialogName(vpid);
} else {
return VASettings.getStubActivityName(vpid);
}
}

由于是Activity,所以会走到VASettings.getStubActivityName(vpid)

    public static String getStubActivityName(int index) {
return String.format(Locale.ENGLISH, "%s$C%d", STUB_ACTIVITY, index);
}

可见,最终返回的Activity名是com.lody.virtual.client.stub.StubActivity$C?,?表示具体数字。

而这个Activity再AndroidManifest.xml中由定义

        <activity
android:name="com.lody.virtual.client.stub.StubActivity$C0"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"
android:process=":p0"
android:taskAffinity="com.lody.virtual.vt"
android:theme="@style/VATheme" />

从标签android:process=":p0"可以得出,新的Activity也是单独开启一个进程,进程名为io.virtualapp:p0

既然重新启动了进程,那么VAPP必然也得走一遍

attachBaseContext -> VirtualCore.get().startup(base) -> invocationStubManager.injectAll();

private void injectInternal() throws Throwable {
if (VirtualCore.get().isMainProcess()) {
......
if (VirtualCore.get().isVAppProcess()) {
addInjector(new LibCoreStub());
addInjector(new ActivityManagerStub());
addInjector(new PackageManagerStub());
addInjector(HCallbackStub.getDefault());
addInjector(new ISmsStub());
addInjector(new ISubStub());
addInjector(new DropBoxManagerStub());
addInjector(new NotificationManagerStub());
addInjector(new LocationManagerStub());
addInjector(new WindowManagerStub());
addInjector(new ClipBoardStub());
addInjector(new MountServiceStub());
addInjector(new BackupManagerStub());
addInjector(new TelephonyStub());
addInjector(new TelephonyRegistryStub());
addInjector(new PhoneSubInfoStub());
addInjector(new PowerManagerStub());
addInjector(new AppWidgetManagerStub());
addInjector(new AccountManagerStub());
addInjector(new AudioManagerStub());
addInjector(new SearchManagerStub());
addInjector(new ContentServiceStub());
addInjector(new ConnectivityStub()); if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {
addInjector(new VibratorStub());
addInjector(new WifiManagerStub());
addInjector(new BluetoothStub());
addInjector(new ContextHubServiceStub());
}
if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
addInjector(new UserManagerStub());
} if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
addInjector(new DisplayStub());
}
if (Build.VERSION.SDK_INT >= LOLLIPOP) {
addInjector(new PersistentDataBlockServiceStub());
addInjector(new InputMethodManagerStub());
addInjector(new MmsStub());
addInjector(new SessionManagerStub());
addInjector(new JobServiceStub());
addInjector(new RestrictionStub());
}
if (Build.VERSION.SDK_INT >= KITKAT) {
addInjector(new AlarmManagerStub());
addInjector(new AppOpsManagerStub());
addInjector(new MediaRouterServiceStub());
}
if (Build.VERSION.SDK_INT >= LOLLIPOP_MR1) {
addInjector(new GraphicsStatsStub());
addInjector(new UsageStatsManagerStub());
}
if (Build.VERSION.SDK_INT >= M) {
addInjector(new FingerprintManagerStub());
addInjector(new NetworkManagementStub());
}
if (Build.VERSION.SDK_INT >= N) {
addInjector(new WifiScannerStub());
addInjector(new ShortcutServiceStub());
addInjector(new DevicePolicyManagerStub());
}
if (Build.VERSION.SDK_INT >= 26) {
addInjector(new AutoFillManagerStub());
}
}
}

最终执行到StubActvity

public abstract class StubActivity extends Activity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
// The savedInstanceState's classLoader is not exist.
super.onCreate(null);
finish();
// It seems that we have conflict with the other Android-Plugin-Framework.
Intent stubIntent = getIntent();
// Try to acquire the actually component information.
StubActivityRecord r = new StubActivityRecord(stubIntent);
if (r.intent != null) {
if (TextUtils.equals(r.info.processName, VirtualRuntime.getProcessName()) && r.userId == VUserHandle.myUserId()) {
// Retry to inject the HCallback to instead of the exist one.
InvocationStubManager.getInstance().checkEnv(HCallbackStub.class);
Intent intent = r.intent;
startActivity(intent);
} else {
// Start the target Activity in other process.
VActivityManager.get().startActivity(r.intent, r.userId);
}
}
} public static class C0 extends StubActivity {
} public static class C1 extends StubActivity {
} public static class C2 extends StubActivity {
} public static class C3 extends StubActivity {
} ... ...
}

在这里把真实的intent给取出来并执行,走入到真正的Activity中,

在替换后,执行流就被改变了,可以看到,在执行到APP的真正Application前,能发现被注入的代码:

com.taobao.sophix_app.MyApplication.onCreate(MyApplication.java:33)
android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1024)
com.lody.virtual.client.hook.delegate.InstrumentationDelegate.callApplicationOnCreate(InstrumentationDelegate.java:225)
com.lody.virtual.client.hook.delegate.AppInstrumentation.callApplicationOnCreate(AppInstrumentation.java:137)
com.lody.virtual.client.VClientImpl.bindApplicationNoCheck(VClientImpl.java:312)
com.lody.virtual.client.VClientImpl.bindApplication(VClientImpl.java:192)
com.lody.virtual.client.hook.proxies.am.HCallbackStub.handleLaunchActivity(HCallbackStub.java:114)
com.lody.virtual.client.hook.proxies.am.HCallbackStub.handleMessage(HCallbackStub.java:71)
android.os.Handler.dispatchMessage(Handler.java:98)
android.os.Looper.loop(Looper.java:154)
android.app.ActivityThread.main(ActivityThread.java:6077)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

virtualapp 应用启动源码分析的更多相关文章

  1. RocketMQ中Broker的启动源码分析(一)

    在RocketMQ中,使用BrokerStartup作为启动类,相较于NameServer的启动,Broker作为RocketMQ的核心可复杂得多 [RocketMQ中NameServer的启动源码分 ...

  2. RocketMQ中Broker的启动源码分析(二)

    接着上一篇博客  [RocketMQ中Broker的启动源码分析(一)] 在完成准备工作后,调用start方法: public static BrokerController start(Broker ...

  3. RocketMQ中PullConsumer的启动源码分析

    通过DefaultMQPullConsumer作为默认实现,这里的启动过程和Producer很相似,但相比复杂一些 [RocketMQ中Producer的启动源码分析] DefaultMQPullCo ...

  4. Django如何启动源码分析

    Django如何启动源码分析 启动 我们启动Django是通过python manage.py runsever的命令 解决 这句话就是执行manage.py文件,并在命令行发送一个runsever字 ...

  5. Quartz源码——scheduler.start()启动源码分析(二)

    scheduler.start()是Quartz的启动方式!下面进行分析,方便自己查看! 我都是分析的jobStore 方式为jdbc的SimpleTrigger!RAM的方式类似分析方式! Quar ...

  6. Netty源码分析 (三)----- 服务端启动源码分析

    本文接着前两篇文章来讲,主要讲服务端类剩下的部分,我们还是来先看看服务端的代码 /** * Created by chenhao on 2019/9/4. */ public final class ...

  7. Seata AT 模式启动源码分析

    从上一篇文章「分布式事务中间件Seata的设计原理」讲了下 Seata AT 模式的一些设计原理,从中也知道了 AT 模式的三个角色(RM.TM.TC),接下来我会更新 Seata 源码分析系列文章. ...

  8. Netty服务端的启动源码分析

    ServerBootstrap的构造: public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, Serve ...

  9. Netty之旅三:Netty服务端启动源码分析,一梭子带走!

    Netty服务端启动流程源码分析 前记 哈喽,自从上篇<Netty之旅二:口口相传的高性能Netty到底是什么?>后,迟迟两周才开启今天的Netty源码系列.源码分析的第一篇文章,下一篇我 ...

  10. 【Netty之旅四】你一定看得懂的Netty客户端启动源码分析!

    前言 前面小飞已经讲解了NIO和Netty服务端启动,这一讲是Client的启动过程. 源码系列的文章依旧还是遵循大白话+画图的风格来讲解,本文Netty源码及以后的文章版本都基于:4.1.22.Fi ...

随机推荐

  1. [转帖]docker-compose完全清除

    https://www.cnblogs.com/gelandesprung/p/12112420.html#:~:text=docker-compose%E5%AE%8C%E5%85%A8%E6%B8 ...

  2. [转帖]服务注册与发现:Nacos Discovery

    目录 一.概述 二.Nacos discovery--服务的注册与发现 1. 版本关系 2. 下载安装 (1)下载 (2)启动 (3)浏览器访问 三.Nacos服务注册与发现实战 1. 构建Sprin ...

  3. [转帖]最全MySQL锁讲解:页锁、共享锁、行锁、表锁、悲观锁、乐观锁

    我们在操作数据库的时候,可能会由于并发问题而引起的数据的不一致性(数据冲突),如何保证数据并发访问的一致性.有效性,是所有数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素,从 ...

  4. 当你对 redis 说你中意的女孩是 Mia

    作者:京东科技 周新智 一.Redis 众所周知,Redis = Remote Dictionary Server,即远程字典服务. 是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久 ...

  5. 浅浅的源码剖析grpc-go(一)

    最近在学习 rpc 相关的知识,如果让我去从头设计一个 rpc,我从使用者的角度出发,究竟需要去做一下什么工作? 第一,RPC 本质上就是一个远程调用,那肯定就需要通过网络来传输数据.虽然传输协议可以 ...

  6. 基于知识图谱的《红楼梦》人物关系可视化及问答系统(含码源):命名实体识别、关系识别、LTP简单教学

    基于知识图谱的<红楼梦>人物关系可视化及问答系统(含码源):命名实体识别.关系识别.LTP简单教学 文件树: app.py是整个系统的主入口 templates文件夹是HTML的页面 |- ...

  7. 21.6 Python 构建ARP中间人数据包

    ARP中间人攻击(ARP spoofing)是一种利用本地网络的ARP协议漏洞进行欺骗的攻击方式,攻击者会向目标主机发送虚假ARP响应包,使得目标主机的ARP缓存中的IP地址和MAC地址映射关系被篡改 ...

  8. 10.1 C++ STL 模板适配与迭代器

    STL(Standard Template Library)标准模板库提供了模板适配器和迭代器等重要概念,为开发者提供了高效.灵活和方便的编程工具.模板适配器是指一组模板类或函数,它们提供一种适配机制 ...

  9. HTTP请求头引发的注入问题 (SQL注入)

    关于请求头中注入问题的演示,这里我写了一些测试案例,用来测试请求头中存在的问题.我们常见的会发生注入的点有 Referer.X-Forwarded-For.Cookie.X-Real-IP.Accep ...

  10. Python 代码推送百度链接

    通过代码实现抓取个人博客中某一页指定文章链接,并批量将该链接推送到百度站长平台,起到快速收录的目的. import sys import requests from bs4 import Beauti ...