应用启动源码分析

在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. 极简版 haproxy的搭建步骤

    背景 发现四层nginx的代理报错. 然后想着换用一下haproxy的配置. 早些时候 看过tidb的一些最佳时间, 这里简单整理一下. 下载 https://src.fedoraproject.or ...

  2. [转帖]高性能 -Nginx 多进程高并发、低时延、高可靠机制在百万级缓存 (redis、memcache) 代理中间件中的应用

    https://xie.infoq.cn/article/2ee961483c66a146709e7e861 关于作者 前滴滴出行技术专家,现任 OPPO 文档数据库 mongodb 负责人,负责 o ...

  3. [转帖]Nginx reuseport 导致偶发性卡顿

    https://github.com/jonmeredith/tcpperf https://plantegg.github.io/2023/06/08/Nginx%20reuseport%20%E5 ...

  4. [转帖]Linux AWK工作原理

    https://www.cnblogs.com/yeyuzhuanjia/p/13967513.html 本篇文章我们主要为大家介绍 AWK 是如何工作的. AWK 工作流程可分为三个部分:1.读输入 ...

  5. [转帖]enq: TX - row lock contention故障处理一则

    https://www.cnblogs.com/zhchoutai/p/7088826.html   一个非常easy的问题,之所以让我对这个问题进行总结.一是由于没我想象的简单,在处理的过程中遇到了 ...

  6. [转帖]OceanBase 在线与离线安装方式详解

    各位好,今天给大家带来一篇有关 OceanBase 在线与离线安装方式的解读.首先我们来讨论一下一日常工作中的一些场景,大家经常会遇到以下几种情况: 公司网络条件很不错,在线下载速度很快,安装软件直接 ...

  7. s-tui验证机器主频的过程

    摘要 小年在家陪孩子. 翻阅<企业存储技术>公众号的文章时 找到了 s-tui 进行监控机器主频的文章 感觉挺有用的 想验证一下 虚拟机有否支持Intel的睿频功能. 所以将之前写的pyt ...

  8. 一文搞懂Go GC演进史,讲的太细致了!

    最近在和 Go就业训练营 的朋友讨论Go GC的问题,发现了刘丹冰老师总结的内容,写的太好了,和大家分享一下. 我们的讨论和思考也整理到这篇文章中了,希望对你有启发. 垃圾回收(Garbage Col ...

  9. 京东云开发者|深入JDK中的Optional

    概述:Optional最早是Google公司Guava中的概念,代表的是可选值.Optional类从Java8版本开始加入豪华套餐,主要为了解决程序中的NPE问题,从而使得更少的显式判空,防止代码污染 ...

  10. 京东云RASP云原生安全免疫创新实践

    作者:京东云 刘一鑫 1 背景 随着网络攻击事件整体呈上升趋势,应用作为网络入口承载着大量业务和流量,因此成为了安全的重灾区.黑客往往借助自动化的工具以及安全漏洞,对Web进行漏洞扫描和探测,进而利用 ...