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。具体函数就比较简单了,大家可以自行查看。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。

IntentService使用及源码分析 - 2016-07-23 14:07:58

IntentService使用及源码分析 转载请注明 原博客地址: http://blog.csdn.net/gdutxiaoxu/article/details/52000680 本篇博客主要简介一下三个问题: 什么是IntentService? 怎样使用IntentService IntentSerice()源码分析 1)什么是IntentService? 我们知道Service和Activity一样是Android的四大组件之一,Service简称为后台服务,具有较高的优先级别。我们平时在Activ

Android——ListView控件 - 2016-07-23 14:07:54

本篇介绍ListView控件,这是Android中比较重要也比较复杂的控件,这里只谈到使用ViewHolder机制优化即可。 一、ListView简介 ListView是Android系统中显示列表的控件,每个ListView都可以包含很多个列表项。 二、ListView的使用 概念不多说,直接来介绍使用方法。 ListView中比较复杂的是数据适配器,其作用是把复杂的数据(数组、链表、数据库、集合等)填充在指定视图界面,是连接数据源和视图界面的桥梁。常见的Android原生的适配器有ArrayAdapt
HTTP请求报文: 一个HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据 1.请求行   请求行由请求 方法字段、URL字段和HTTP协议版本字段 3个字段组成,它们用空格分隔。比如 GET /data/info.html HTTP/1.1 方法字段就是HTTP使用的请求方法,比如常见的GET/POST 其中HTTP协议版本有两种:HTTP1.0/HTTP1.1 可以这样区别: HTTP1.0对于每个连接都的建立一次连接一次只能传送一个请求和响应,请求就会关闭,HTTP1.0没有Host字

gradle多渠道打包 - 2016-07-23 14:07:48

E文不好的童鞋,例如我,翻译文章的过程里没有愉悦的感受,只有2行老泪;但最终有一丝成就感也算是安慰了。所以我会去尊重那些翻译IT技术文章的大拿们,他们就是千千万万个亚里士多德和吴启明,他们是E文不好的童鞋的传教士,阿门,当然我不是大拿。 废话少说,先看一篇例子:在 http://ghui.me/post/2015/03/create-several-variants/  。 然后来看这篇翻译,扫清例子中一部分未知的知识。原文在 http://tools.android.com/tech-docs/new-b
前言 本文主要讲解Telephony中Phone相关的知识,主要想讲明白三件事情: Phone是什么? Phone从哪里来? Phone有什么作用? 1. Phone是什么 1.1 Phone是一个接口 Phone.java (frameworks\opt\telephony\src\java\com\android\internal\telephony) public interface Phone { //包含了大量的register/unregister的方法。(监听能力) void registe

Android混淆心得 - 2016-07-23 14:07:36

最近在做Android应用的混淆,踩了一些坑,这里记录分享下个人的心得。 混淆介绍 首先先简单说一下什么是混淆和混淆的作用,其实这个搜索下可以找到一堆官方的说法等等,这里简单口语叙述一下,混淆就是把代码替换成a、b、c基本字母组成的代码,比如一个方法名为:function(),混淆后可能会被替换成a()。 混淆的好处: 代码混淆后阅读性降低,反编译后破译程序难度提高 混淆后字节数减少,减少了应用了体积 前者只能说有一点作用,后者则需要看代码的数量 当然不能忽视混淆的缺点: 混淆后,测试不充分可能导致某些功
[编写高质量iOS代码的52个有效方法](二)对象 参考书籍:《Effective Objective-C 2.0》 【英】 Matt Galloway 先睹为快 6.理解“属性”这一概念 7.在对象内部尽量直接访问实例变量 8.理解“对象等同性”这一概念 9.以“类簇模式”隐藏实现细节 10.在既有类中使用关联对象存放自定义数据 目录 编写高质量iOS代码的52个有效方法二对象 先睹为快 目录 第6条理解属性这一概念 第7条在对象内部尽量直接访问实例变量 第8条理解对象等同性这一概念 第9条以类簇模式隐
插件其实是Apk安装包,如果要使用必须先要安装和解析,以便知道插件Apk的相关信息。而从Demo中我们知道插件的安装和卸载是通过调用PluginManager的installPackage()和deletePackage()来实现的。就先从PluginManager.installPackage()开始分析插件Apk的安装过程。 第一步:PluginManager. getInstance().installPackage(apkPath,flag); 此函数中只是调用了mPluginManager.in
分享截屏已经是很多游戏应用必备的功能了,找到了一个国内的插件,虽然用起来还行,但是,还是想吐槽下,跟老外的插件比,真的有差距啊有差距啊有差距啊,啊啊啊。 ShareSDK的官方网站:http://www.mob.com/,使用插件需要注册账号获得key,不过,至少现在是免费的。 我的Unity版本是5.3,xcode版本是7.2,ShareSDK版本是2.7.4 Unity sdk下载地址:https://github.com/MobClub/New-Unity-For-ShareSDK 新建一个简单的工
正常情况下启动一个Activity,首先需要在AndroidManifest文件中声明,其次需要把该应用安装到手机系统中。 而插件apk是没有正在安装到手机系统中的,也就按照正常的启动流程插件Activity是不能启动的。另外插件apk的类需要加载进来是需要指定ClassLoader。前面的文章也大概讲过,当启动一个插件Activity时,先是用预定义的代理Activity替换目标Activity(及插件Activity)去启动,当AMS处理完回调到应用空间时(及回到运行Activity的进程空间时)再用