DroidPlugin源码分析服务与静态广播的处理

上一篇文章分析过DroidPlugin对Activity的处理过程,不得不为对DroidPlugin的工程师们钦佩不已,那么是不是Service可以像Activity的处理过程一样来处理呢?前面讲过每一个代理进程只是预定义了一个Service,如果某一个插件中有多个Service,那岂不是某一个时刻只能有一个Service运行呢?由此可以判定可能Service的处理和Activity不一样。

一方面:平时使用Activity主要是用于展示界面和用户交互,Activity的生命周期可能受用户控制,当用户操作界面进入新界面或返回界面时。也可能受系统控制,进入锁屏界面时,或者来电话时。也就是说Activity生命周期与用户和系统都是强关系。而Service呢?启动或停止,完全由我们主动控制,也就是说Service与用户和系统都是弱关系。所以我们完全可以无需经过系统手动调用Service的生命周期来让他运行。

另一方面:我们一直说Service是运行在后台提供服务的。其实正在提供服务的并不是Service,而是在Service内实例化的Binder本地对象。包括Service的生命周期也是为Binder本地对象服务的。也就是说我们可以在一个Service里面实例化多个Binder提供服务,说白了我们可以在一个Service里面通过手动控制Service生命周期运行多个Service。前提是我们需要对这些Service管理起来。

有了上面的理解,接下来就以StartService为例分析DroidPlugin对Service的处理。

StartService和StartActivity一样都是通过调用AMS代理对象请求AMS服务的。那么不多说了直接看分发Hook AMS的类IActivityManagerHookHandle的内部类startService的beforeInvoke函数。

在beforeInvoke函数中只是调用了replaceFirstServiceIntentOfArgs函数:

    private static ServiceInfo replaceFirstServiceIntentOfArgs(Object[] args) throws RemoteException {
        int intentOfArgIndex = findFirstIntentIndexInArgs(args);
        if (args != null && args.length > 1 && intentOfArgIndex >= 0) {
            Intent intent = (Intent) args[intentOfArgIndex];
            ServiceInfo serviceInfo = resolveService(intent);
            if (serviceInfo != null && isPackagePlugin(serviceInfo.packageName)) {
                ServiceInfo proxyService = selectProxyService(intent);
                if (proxyService != null) {
                    Intent newIntent = new Intent();
                    newIntent.setAction(proxyService.name + new Random().nextInt());

                    newIntent.setClassName(proxyService.packageName, proxyService.name);
                    newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
                    newIntent.setFlags(intent.getFlags());
                    args[intentOfArgIndex] = newIntent;
                    return serviceInfo;
                }
            }
        }
        return null;
}

分析过Activity的流程,这个函数就不难分析了:

主要工作如下:

1 从参数中找到Intent的位置,然后取出Intent对象,然后通过Intent对象获得对应的ServiceInfo对象。

2 判断 当前service 的包名是不是已近安装的插件apk包名。

3 选择一个合适的代理服务,来替换要启动的目标服务。选择的过程之前分析过Activity,Service的基本类似这里不做过多分析,这里需要特别说一下的就是选择的Service必然是继承自AbstractServiceStub。分析完这个函数再说AbstractServiceStub隐藏的秘密。

4 创建一个Intent,设置启动服务为代理服务,将目标服务保存到代理服务intent中。

5 把新创建的代理Intent替换原来参数Intent的位置。由此可以骗过系统检查。

至此,目标服务已经被代理服务所替代,AMS启动完毕后,就会调用代理服务的onCreate, onStart。

前面提到所有预定义的代理服务都是继承自AbstractServiceStub。接下来看隐藏的秘密。

AbstractServiceStub的onCreate函数

这个函数没有做太多的事情,只是设置isRunning 为True。

AbstractServiceStub的onStart函数如下:

    public void onStart(Intent intent, int startId) {
        try {
            if (intent != null) {
                if (intent.getBooleanExtra("ActionKillSelf", false)) {
                    startKillSelf();
                    if (!ServcesManager.getDefault().hasServiceRunning()) {
                        stopSelf(startId);
                        boolean stopService = getApplication().stopService(intent);
}
                } else {
                    mCreator.onStart(this, intent, 0, startId);
                }
            }
        } catch (Throwable e) {
            handleException(e);
        }
        super.onStart(intent, startId);
}

这个函数主要做了如下工作:

是否记得在分析进程管理的时候当一个进程中Activity数为零,且Service数不为零是就会发送一个Intent 设置ActionKillSelf为True,查看是否存在插件Service在运行,如果没有则关闭Service,同时kill进程。

1 判断Intent中是否存在ActionKillSelf参数,且为True。

如果为True,先启动一个线程,然后等待服务Destory之后,再调用杀掉运行该服务的预定义插件进程。

2 判断是否还有插件服务在该进程中运行,如果有返回,如果没有则停止当前服务。这样自然就会调用该服务的onDestory函数,这样第一步的线程就会继续执行,然后停止该进程。

3 在StartService中,ActionKillSelf是没有的,则为false,因此会执行

mCreator.onStart(this, intent, 0, startId);这行代码。

mCreator是一个ServcesManager类对象。

接下来看ServcesManager的onStart函数。

    public int onStart(Context context, Intent intent, int flags, int startId) throws Exception {
        Intent targetIntent = intent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);
        if (targetIntent != null) {
            ServiceInfo targetInfo = PluginManager.getInstance().resolveServiceInfo(targetIntent, 0);
            if (targetInfo != null) {
                Service service = mNameService.get(targetInfo.name);
                if (service == null) {
                    handleCreateServiceOne(context, intent, targetInfo);
                }
                handleOnStartOne(targetIntent, flags, startId);
            }
        }
        return -1;
    }

这个函数主要做了如下工作:

1 通过intent 找到要启动的目标Intent。然后通过目标Intent找到目标服务的serviceInfo.

2 以目标服务的名字从mNameService查找要启动的目标服务是否已经启动。

3 如果没有启动则调用handleCreateServiceOne 创建该服务。

4如果目标服务已经缓存,说明已经启动了,或者在创建目标服务完成后,调用handleOnStartOne调用服务的onStart函数

先分析handleCreateServiceOne函数具体创建服务的过程

    private void handleCreateServiceOne(Context hostContext, Intent stubIntent, ServiceInfo info) throws Exception {
        ResolveInfo resolveInfo = hostContext.getPackageManager().resolveService(stubIntent, 0);
        ServiceInfo stubInfo = resolveInfo != null ? resolveInfo.serviceInfo : null;
        PluginManager.getInstance().reportMyProcessName(stubInfo.processName, info.processName, info.packageName);
        PluginProcessManager.preLoadApk(hostContext, info);
        Object activityThread = ActivityThreadCompat.currentActivityThread();
        IBinder fakeToken = new MyFakeIBinder();
        Class CreateServiceData = Class.forName(ActivityThreadCompat.activityThreadClass().getName() + "$CreateServiceData");
        Constructor init = CreateServiceData.getDeclaredConstructor();
        if (!init.isAccessible()) {
            init.setAccessible(true);
        }
        Object data = init.newInstance();

        FieldUtils.writeField(data, "token", fakeToken);
        FieldUtils.writeField(data, "info", info);
        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
            FieldUtils.writeField(data, "compatInfo", CompatibilityInfoCompat.DEFAULT_COMPATIBILITY_INFO());
        }

        Method method = activityThread.getClass().getDeclaredMethod("handleCreateService", CreateServiceData);
        if (!method.isAccessible()) {
            method.setAccessible(true);
        }
        method.invoke(activityThread, data);
        Object mService = FieldUtils.readField(activityThread, "mServices");
        Service service = (Service) MethodUtils.invokeMethod(mService, "get", fakeToken);
        MethodUtils.invokeMethod(mService, "remove", fakeToken);
        mTokenServices.put(fakeToken, service);
        mNameService.put(info.name, service);

        if (stubInfo != null) {
            PluginManager.getInstance().onServiceCreated(stubInfo, info);
        }
}

这个函数基本的工作如下:

1 先找到代理服务的ServiceInfo, 然后调用PluginManager.getInstance().reportMyProcessName(stubInfo.processName, info.processName, info.packageName); 关联包名和目标进程和代理进程信息,方便管理。在进程管理一文中分析过的。

2 PluginProcessManager.preLoadApk(hostContext, info) 设置LoadedApk,以及ClassLoader, 这个在插件对Activity的处理中已有分析。

3 通过查看源码可以了解如下信息,当启动一个服务,回到服务运行的应用空间来的时候(其实就是运行服务的进程中),创建服务的过程,是通过ActivityThread 的handleCreateService函数来创建的,同时在调用这个函数的时候需要传入CreateServiceData 数据类,在这个数据类中有一个iBinder对象,这个对象其实在ActivityManagerService中是一个ServiceRecord,主要用来记录一个服务信息的。当调用ActivityThread的函数handleCreateService创建服务完成之后,

A:会调用该服务的OnCreate函数。

B: 服务会以ServiceRecord 这个iBinder对象为Key Service对象为Value保存在ActivityThread的mService成员变量中。

有了这些了解以后,下面的代码主要完成如下工作:

4 目标服务实际是没有在ActivityManager中启动的,所以没有ServiceRecord对象,所以会先伪造一个IBinder对象MyFakeIBinder。

5 创建一个CreateServiceData 类对象。并把伪造的IBinder对象和目标ServiceInfo保存到该数据对象中。

6 调用ActivityThread函数handleCreateService创建目标服务对象。

7 从ActivityThread的成员变量mService中以前面伪造的IBinder对象为Key从里面取出刚刚创建的服务。

8 以伪造的IBinder对象和以目标服务类名为key分别保存到ServiceManager的成员变量mTokenServices和mNameService中。

9 调用PluginManager.getInstance().onServiceCreated(stubInfo, info);对刚刚启动的服务,和运行服务的进程进行管理。前面的进程管理的文章中已经有对Activity分析,Service基本类似。

handleOnStartOne调用Service的onStart函数的过程。

    private void handleOnStartOne(Intent intent, int flags, int startIds) throws Exception {
        ServiceInfo info = PluginManager.getInstance().resolveServiceInfo(intent, 0);
        if (info != null) {
            Service service = mNameService.get(info.name);
            if (service != null) {
                ClassLoader classLoader = getClassLoader(info.applicationInfo);
                intent.setExtrasClassLoader(classLoader);
                Object token = findTokenByService(service);
                Integer integer = mServiceTaskIds.get(token);
                if (integer == null) {
                    integer = -1;
                }
                int startId = integer + 1;
                mServiceTaskIds.put(token, startId);
                int res = service.onStartCommand(intent, flags, startId);
                QueuedWorkCompat.waitToFinish();
            }
        }
}

这个函数主要的工作如下:

1 通过Intent找到ServiceInfo,然后通过ServiceINfo的name从mNameService中找到service.

获取ClassLoader 设置Intent的ClassLoader。

2 通过service找到Token,然后通过Token在mServiceTaskIds查看是否设置startId.

3 如果为空则以token为key设置startId为Value保存到ServiceManger成员变量mServiceTaskIds中。

4 最后调用service,onstartCommend函数。

5 调用QueuedWork.waittoFinish等待完成。

至此插件对Service的处理分析完成。

DroidPlugin对静态广播的处理:

在DroidPlugin的文档中已经说过了,他们对静态广播的处理是通过动态注册的方式来实现的。也就是说DroidPlugin只是解析了插件Apk AndroidManifest文件中的静态广播,进行动态注册。

具体注册过程是在调用Application onCreate函数的时候。

当LoadedApk调用makeApplication函数的时候,会调用instrumentation.callApplicationOnCreate,而instrumentation已经被PluginInstrumentation篡改。就在PluginInstrumentation的callApplicationOnCreate函数中。会调用PluginProcessManager.registerStaticReceiver。具体函数就比较简单了,大家可以自行查看。

时间: 2024-10-13 18:53:22

DroidPlugin源码分析服务与静态广播的处理的相关文章

DroidPlugin源码分析服务与静态广播的处理214r3rff4

上一篇文章分析过DroidPlugin对Activity的处理过程,不得不为对DroidPlugin的工程师们钦佩不已,那么是不是Service可以像Activity的处理过程一样来处理呢?前面讲过每一个代理进程只是预定义了一个Service,如果某一个插件中有多个Service,那岂不是某一个时刻只能有一个Service运行呢?由此可以判定可能Service的处理和Activity不一样. 一方面:平时使用Activity主要是用于展示界面和用户交互,Activity的生命周期可能受用户控制,

DroidPlugin源码分析插件运行环境初始化

从DroidPlugin的官方文档中我们知道. 2 在AndroidManifest.xml中使用插件的com.morgoo.droidplugin.PluginApplication: 或者在自定义的Application的onCreate()函数中,调用PluginHelper.getInstance().applicationOnCreate(getBaseContext()); 在Application的attachBaseContext()函数中,调用 PluginHelper.get

插件开发之360 DroidPlugin源码分析(五)Service预注册占坑

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52264977 在了解系统的activity,service,broadcastReceiver的启动过程后,今天将分析下360 DroidPlugin是如何预注册占坑的?本篇文章主要分析Service预注册占坑,Service占了坑后又是什么时候开始瞒天过海欺骗AMS的?先看下Agenda: AndroidMainfest.xml中概览 Service中关键方法被h

插件开发之360 DroidPlugin源码分析(四)Activity预注册占坑

请尊重分享成果,转载请注明出处: http://blog.csdn.net/hejjunlin/article/details/52258434 在了解系统的activity,service,broadcastReceiver的启动过程后,今天将分析下360 DroidPlugin是如何预注册占坑的?本篇文章主要分析Activity预注册占坑,Activity占了坑后又是什么时候开始瞒天过海欺骗AMS的?先看下Agenda: AndroidMainfest.xml中概览 Activity中关键方

SOFA 源码分析 —— 服务引用过程

前言 在前面的 SOFA 源码分析 -- 服务发布过程 文章中,我们分析了 SOFA 的服务发布过程,一个完整的 RPC 除了发布服务,当然还需要引用服务. So,今天就一起来看看 SOFA 是如何引用服务的.实际上,基础逻辑和我们之前用 Netty 写的 RPC 小 demo 类似.有兴趣可以看看这个 demo-- 自己用 Netty 实现一个简单的 RPC. 示例代码 ConsumerConfig<HelloService> consumerConfig = new ConsumerCon

DroidPlugin源码分析插件进程管理以及预注册Activity,Service,ContentProvide的选择

在360对DroidPlugin的特点介绍中有云: 插件的四大组件完全不需要在Host程序中注册,支持Service.Activity.BroadcastReceiver.ContentProvider四大组件. 实现了进程管理,插件的空进程会被及时回收,占用内存低. 之所以支持Service,Activity,ContentProvider三大组件,是因为DroidPlugin在AndroidManifest文件中预先注册了8个运行插件的进程,每个进程预注册Service一个, Content

netty 5 alph1源码分析(服务端创建过程)

参照<Netty系列之Netty 服务端创建>,研究了netty的服务端创建过程.至于netty的优势,可以参照网络其他文章.<Netty系列之Netty 服务端创建>是 李林锋撰写的netty源码分析的一篇好文,绝对是技术干货.但抛开技术来说,也存在一些瑕疵. 缺点如下 代码衔接不连贯,上下不连贯. 代码片段是截图,对阅读代理不便(可能和阅读习惯有关) 本篇主要内容,参照<Netty系列之Netty 服务端创建>,梳理出自己喜欢的阅读风格. 1.整体逻辑图 整体将服务

SOFA 源码分析 —— 服务发布过程

前言 SOFA 包含了 RPC 框架,底层通信框架是 bolt ,基于 Netty 4,今天将通过 SOFA-RPC 源码中的例子,看看他是如何发布一个服务的. 示例代码 下面的代码在 com.alipay.sofa.rpc.quickstart.QuickStartServer 类下. ServerConfig serverConfig = new ServerConfig() .setProtocol("bolt") // 设置一个协议,默认bolt .setPort(9696)

Dubbo 源码分析 - 服务导出全过程解析

1.服务导出过程 本篇文章,我们来研究一下 Dubbo 导出服务的过程.Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三个部分,第一是前置工作,主要用于检查参数,组装 URL.第二是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程.第三是向注册中心注册服务,用于服务发现.本篇文章将会对这三个部分代码进行详细的分析,在分析之前,我们先来了解一下服务的导出过程. Dubbo 支持两种服务导出方式,