Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service
上一章: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 调度中其实没有发挥什么作用。原因有以下几点:
- 首先 VA 内部的插件 Service 没有比较暴露给外部 App 调用,所以让 AMS 知晓 Service 的意义不大。
其次和 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);}
这里主要做了以下几个工作:
- 和 Activity 创建的时候一样,调用 startProcessIfNeedLocked 检查 Application 是否初始化,没有则开始初始化 Application 流程。
- 准备 ServiceRecord 和 ServiceInfo。
- 如果 service 还没有创建,则直接调用 ApplicationThread.scheduleCreateService 创建 Service,可以看出这里直接跳过了 AMS。
- 将 ServiceRecord 记录到 Service 列表,等待 bindService,如果是通过 bindService 自动创建的 Service,在创建 Service 完成后会进入 bindService 流程。
同样的 bindService 也是直接调用系统的 ApplicationThread.scheduleBindService
好了,由于 Service 的特点,startService 看上去比 startActivity 简单多了。接下来要分析的是 BroacastReceiver。
下一章:Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver
Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service相关推荐
- Android 双开沙箱 VirtualApp 源码分析(二)
Android 双开沙箱 VirtualApp 源码分析(二) VA 初始化 先看一下代码: VirtualCore.startup public void startup(Context conte ...
- Android 双开沙箱 VirtualApp 源码分析(一)
最近发现了一个非常好的开源项目,基本实现了一个 Android 上的沙箱环境,不过应用场景最多的还是应用双开. VA github: https://github.com/asLody/Virtual ...
- Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider
上一章:Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver Provider 注册 回顾前面,Activity 启动的时候会检查 Application ...
- 【投屏】Scrcpy源码分析四(最终章 - Server篇)
Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...
- Android录音下————AudioRecord源码分析
Android录音下----AudioRecord源码分析 文章目录 Android录音下----AudioRecord源码分析 一.概述 1.主要分析点 2.储备知识 二.getMinBufferS ...
- Android上百实例源码分析以及开源分析集合打包
感谢网友banketree的收集,压缩包的内容如下: 1.360新版特性界面源代码 实现了360新版特性界面的效果,主要涉及到Qt的一些事件处理与自定义控件.但源码好像是c++. 2.aidl跨进程调 ...
- Android 8.0系统源码分析--Camera processCaptureResult结果回传源码分析
相机,从上到下概览一下,真是太大了,上面的APP->Framework->CameraServer->CameraHAL,HAL进程中Pipeline.接各种算法的Node.再往下的 ...
- Android主流三方库源码分析(九、深入理解EventBus源码)
一.EventBus使用流程概念 1.Android事件发布/订阅框架 2.事件传递既可用于Android四大组件间通信 3.EventBus的优点是代码简洁,使用简单,事件发布.订阅充分解耦 4.首 ...
- Android Camera 系统架构源码分析
Android Camera 系统架构源码分析(1)---->Camera的初始化 Android Camera 系统架构源码分析(2)---->Camera的startPreview和s ...
最新文章
- 知乎如何洞察你的真实喜好?首页信息流技术揭秘
- 删除重复字符串的算法
- futurejava前台_web前端页面与后端Java的数据交互
- java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent
- mvn spring-boot:run 增加jvm启动参数
- Shell基本命令汇总
- android开发之AsyncTask的用法
- 数据结构之删除线性表中的元素
- Nginx+Tomcat实现单IP、多域名、多站点的访问
- 论文准备:基于区块链的一些设计IIoT的最新动向调查【已公开发表】
- Socket编程之聊天室
- 2020第六届上海市大学生网安大赛Misc|writeup
- B站股权曝光:陈睿持股12.9%阿里持股7.9% 寻求双重主要上市
- C# Control.Refresh的解释是什么意思?
- python外国网站爬虫_用python爬过这些网站,才敢说自己会爬虫!
- button layui-btn 色调
- unity实现游戏中拍照功能(自动生成小照片)
- hdu2795 线段树应用:找到线段树中=给定值的第一个元素位置 并 更新该点)
- linux镜像文件32,centos7光盘镜像下载32/64位
- PaaS平台升级NFS报错排除