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

原生 Service 创建过程

首先有必要了解一下原生 framework 对 Service 的创建,因为在 VA 中启动 Service 和 Activity 有很大的区别。

首先入口 ContextWrapper.startService():

@Overridepublic ComponentName startService(Intent service) {return mBase.startService(service);}

mBase 是 ContextImpl,所以调用到 ContextImpl.startService():

 @Overridepublic ComponentName startService(Intent service) {warnIfCallingFromSystemProcess();return startServiceCommon(service, mUser);}private ComponentName startServiceCommon(Intent service, UserHandle user) {try {validateServiceIntent(service);service.prepareToLeaveProcess(this);ComponentName cn = ActivityManagerNative.getDefault().startService(mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), getOpPackageName(), user.getIdentifier());if (cn != null) {if (cn.getPackageName().equals("!")) {throw new SecurityException("Not allowed to start service " + service+ " without permission " + cn.getClassName());} else if (cn.getPackageName().equals("!!")) {throw new SecurityException("Unable to start service " + service+ ": " + cn.getClassName());}}return cn;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

Client 的流程最后到 ActivityManagerNative.startService():

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IActivityManager.descriptor);data.writeStrongBinder(caller != null ? caller.asBinder() : null);data.writeString(callingPackage);intent.writeToParcel(data, 0);data.writeString(resolvedType);data.writeStrongBinder(resultTo);data.writeString(resultWho);data.writeInt(requestCode);data.writeInt(startFlags);if (profilerInfo != null) {data.writeInt(1);profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);} else {data.writeInt(0);}if (options != null) {data.writeInt(1);options.writeToParcel(data, 0);} else {data.writeInt(0);}// 远程调用 AMSmRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);reply.readException();int result = reply.readInt();reply.recycle();data.recycle();return result;}

不出所料,逻辑再一次转移到远程的 AMS 中,然后我们先忽略 AMS 中的一堆逻辑,最后 AMS 调用到这:

    private final void realStartServiceLocked(ServiceRecord r,ProcessRecord app, boolean execInFg) throws RemoteException {app.thread.scheduleCreateService(r, r.serviceInfo,mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),app.repProcState);sendServiceArgsLocked(r, execInFg, true);}

这里的 ProcessRecoder.thread 是 AMS 持有的 Client App 的 IBinder 句柄,通过他可以远程调用到 Client App 的 ApplicationThread 中的 scheduleCreateService 方法:

public final void scheduleCreateService(IBinder token,ServiceInfo info, CompatibilityInfo compatInfo, int processState) {updateProcessState(processState, false);CreateServiceData s = new CreateServiceData();s.token = token;s.info = info;s.compatInfo = compatInfo;sendMessage(H.CREATE_SERVICE, s);}

这里大家可能发现了,Intent 在 AMS 绕了一圈又回来了,事实上 AMS 在其中好像没有发挥什么作用,其实在外部环境 AMS 还是很重要的,但是在 VA 中,AMS 在 Service 调度中其实没有发挥什么作用。原因有以下几点:

  1. 首先 VA 内部的插件 Service 没有比较暴露给外部 App 调用,所以让 AMS 知晓 Service 的意义不大。
  2. 其次和 Activity 必须有个 StubActivity 让 AMS 持有不一样,Service 生命周期和功能都极其简单,并且没有界面,没有交互,换句话说 Service 和其他 Framework Service(例如 WMS) 没有任何关系,所以其实并不需要 AMS 这一步存在。

    那么综上所诉,为了启动插件 Service 我们其实可以绕过 AMS,直接调用 ApplicationThread 中的 scheduleCreateService 方法,Service 的会话储存交给 VAMS 就行。

startService 的实现

和 startActivity 一样,首先是 Hook:

static class StartService extends MethodProxy {@Overridepublic String getMethodName() {return "startService";}@Overridepublic 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 processreturn method.invoke(who, args);}int userId = VUserHandle.myUserId();// 如果是内部请求,获取原来的 Serviceif (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) {// 远程调用 VAMS.startService()return VActivityManager.get().startService(appThread, service, resolvedType, userId);}return method.invoke(who, args);}@Overridepublic boolean isEnable() {return isAppProcess() || isServerProcess();}}

和 startActivity 一样将真正业务交给 VAMS.startService():

 private ComponentName startServiceCommon(Intent service,boolean scheduleServiceArgs, int userId) {ServiceInfo serviceInfo = resolveServiceInfo(service, userId);if (serviceInfo == null) {return null;}ProcessRecord targetApp = startProcessIfNeedLocked(ComponentUtils.getProcessName(serviceInfo),userId,serviceInfo.packageName);if (targetApp == null) {VLog.e(TAG, "Unable to start new Process for : " + ComponentUtils.toComponentName(serviceInfo));return null;}IInterface appThread = targetApp.appThread;ServiceRecord r = findRecordLocked(userId, serviceInfo);boolean needCreateService = false;if (r == null) {r = new ServiceRecord();r.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);r.startId = 0;r.activeSince = SystemClock.elapsedRealtime();r.process = targetApp;r.serviceInfo = serviceInfo;needCreateService = true;} else {if (r.process == null) {r.process = targetApp;needCreateService = true;}}// 如果 service 尚未创建if (needCreateService) {try {// 调用 ApplicationThread.scheduleCreateService 直接创建 ServiceIApplicationThreadCompat.scheduleCreateService(appThread, r, r.serviceInfo, 0);} catch (RemoteException e) {e.printStackTrace();}// Note: If the service has been called for not AUTO_CREATE binding, the corresponding// ServiceRecord is already in mHistory, so we use Set to replace List to avoid add// ServiceRecord twice// 将 ServiceRecorder 推入 historyaddRecord(r);// 等待 bindService,如果是通过 bindService 自动创建的 Service,在创建 Service 完成后会进入 bindService 流程requestServiceBindingsLocked(r);}r.lastActivityTime = SystemClock.uptimeMillis();if (scheduleServiceArgs) {r.startId++;boolean taskRemoved = serviceInfo.applicationInfo != null&& serviceInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.ECLAIR;try {IApplicationThreadCompat.scheduleServiceArgs(appThread, r, taskRemoved, r.startId, 0, service);} catch (RemoteException e) {e.printStackTrace();}}return ComponentUtils.toComponentName(serviceInfo);}

这里主要做了以下几个工作:

  1. 和 Activity 创建的时候一样,调用 startProcessIfNeedLocked 检查 Application 是否初始化,没有则开始初始化 Application 流程。
  2. 准备 ServiceRecord 和 ServiceInfo。
  3. 如果 service 还没有创建,则直接调用 ApplicationThread.scheduleCreateService 创建 Service,可以看出这里直接跳过了 AMS。
  4. 将 ServiceRecord 记录到 Service 列表,等待 bindService,如果是通过 bindService 自动创建的 Service,在创建 Service 完成后会进入 bindService 流程。

同样的 bindService 也是直接调用系统的 ApplicationThread.scheduleBindService

好了,由于 Service 的特点,startService 看上去比 startActivity 简单多了。接下来要分析的是 BroacastReceiver。

下一章:Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver

Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service相关推荐

  1. Android 双开沙箱 VirtualApp 源码分析(二)

    Android 双开沙箱 VirtualApp 源码分析(二) VA 初始化 先看一下代码: VirtualCore.startup public void startup(Context conte ...

  2. Android 双开沙箱 VirtualApp 源码分析(一)

    最近发现了一个非常好的开源项目,基本实现了一个 Android 上的沙箱环境,不过应用场景最多的还是应用双开. VA github: https://github.com/asLody/Virtual ...

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

    上一章:Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver Provider 注册 回顾前面,Activity 启动的时候会检查 Application ...

  4. 【投屏】Scrcpy源码分析四(最终章 - Server篇)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

  5. Android录音下————AudioRecord源码分析

    Android录音下----AudioRecord源码分析 文章目录 Android录音下----AudioRecord源码分析 一.概述 1.主要分析点 2.储备知识 二.getMinBufferS ...

  6. Android上百实例源码分析以及开源分析集合打包

    感谢网友banketree的收集,压缩包的内容如下: 1.360新版特性界面源代码 实现了360新版特性界面的效果,主要涉及到Qt的一些事件处理与自定义控件.但源码好像是c++. 2.aidl跨进程调 ...

  7. Android 8.0系统源码分析--Camera processCaptureResult结果回传源码分析

    相机,从上到下概览一下,真是太大了,上面的APP->Framework->CameraServer->CameraHAL,HAL进程中Pipeline.接各种算法的Node.再往下的 ...

  8. Android主流三方库源码分析(九、深入理解EventBus源码)

    一.EventBus使用流程概念 1.Android事件发布/订阅框架 2.事件传递既可用于Android四大组件间通信 3.EventBus的优点是代码简洁,使用简单,事件发布.订阅充分解耦 4.首 ...

  9. Android Camera 系统架构源码分析

    Android Camera 系统架构源码分析(1)---->Camera的初始化 Android Camera 系统架构源码分析(2)---->Camera的startPreview和s ...

最新文章

  1. 知乎如何洞察你的真实喜好?首页信息流技术揭秘
  2. 删除重复字符串的算法
  3. futurejava前台_web前端页面与后端Java的数据交互
  4. java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent
  5. mvn spring-boot:run 增加jvm启动参数
  6. Shell基本命令汇总
  7. android开发之AsyncTask的用法
  8. 数据结构之删除线性表中的元素
  9. Nginx+Tomcat实现单IP、多域名、多站点的访问
  10. 论文准备:基于区块链的一些设计IIoT的最新动向调查【已公开发表】
  11. Socket编程之聊天室
  12. 2020第六届上海市大学生网安大赛Misc|writeup
  13. B站股权曝光:陈睿持股12.9%阿里持股7.9% 寻求双重主要上市
  14. C# Control.Refresh的解释是什么意思?
  15. python外国网站爬虫_用python爬过这些网站,才敢说自己会爬虫!
  16. button layui-btn 色调
  17. unity实现游戏中拍照功能(自动生成小照片)
  18. hdu2795 线段树应用:找到线段树中=给定值的第一个元素位置 并 更新该点)
  19. linux镜像文件32,centos7光盘镜像下载32/64位
  20. PaaS平台升级NFS报错排除

热门文章

  1. 宅在家太无聊了,我用Python做了一个能作弊的抽奖程序
  2. html密码输入框位数,JavaScript仿支付宝6位数字密码输入框
  3. M-Arch(番外12)GD32L233评测-CAU加解密(捉弄下小编)
  4. OPC Expert v8.1.2211 Crack
  5. word文档四种解锁方法
  6. 人类登月的幕后英雄,又推出了太空中第一位AI暖男
  7. 官宣!英伟达终止收购ARM,后者开始准备IPO
  8. OSPFv3基础和与v2的对比
  9. mysql数据库 导航页
  10. electron-vue 安装打包心得