Binder进程间通信系统第五篇-----Binder进程间通信实例

☆樱花仙子☆ 提交于 2020-04-08 09:21:17

注意1
class IFregService: public IInterface 服务接口是为 class BnFregService: public BnInterface 服务的,如下声明和定义的服务接口,是为FregService服务提供的接口,这是Binder进程间通信的基本要求,Binder进程间通信机制要求提供服务的一方必须提供一个跨进程访问能力的服务接口,以便使用服务的一方可以通过该接口来访问服务。接口名就是 descriptor : " hr.ma.IFregService " 。客户进程可以在 service manager中通过该 名称 获取服务的接口,然后访问服务。

DECLARE_META_INTERFACE(FregService);
/*
* 0. 使用宏 IMPLEMENT_META_INTERFACE 来实现IFregService类的元接口
* 1. 将 IFregService类的静态成员变量 descriptor 设置为  " hr.ma.IFregService " 
* 2. 实现了 IFregService类的构造函数和析构函数,是空函数
* 3. 实现了成员函数 getInterfaceDescriptor() ,用来获取一个IFregService类的描述符,即 descriptor 
* 4. 实现了  asInterface(),用来将一个 IBinder对象装换为一个 IFregService接口。
*/
IMPLEMENT_META_INTERFACE(FregService, "hr.ma.IFregService");
1
2
3
4
5
6
7
8
9
注意2
CHECK_INTERFACE函数

BnFregService类的成员函数 onTransact在将 GET_VAL和SET_VAL进程间通信请求发给其子类处理之前,会首先调用宏 CHECK_INTERFACE 来检查该进程间通信求情的合法性,即检查该请求是否是由 FregService组件的代理对象发送过来的。如果是,那么 传递过来的Parcel对象data中的第一个数据应该是一个 IFregService接口描述符,即 “hr.ma.IFregService” 。如果不是,那么
BnFregService类的成员函数 onTransact就会认为这是一个非法的进程间通信请求,就不会继续执行下去。

注意3 框架大致如下

本文将基于应用程序框架层提供的 Binder库来开发一个Binder进程间通信应用实例,它包含一个Server进程和一个Client进程。其中Server进程实现了一个Service组件,负责管理我前面写的虚拟硬件设备 freg的寄存器 val,并且向Client进程提供访问服务。

之前写的虚拟硬件设备博客:https://blog.csdn.net/LinuxArmbiggod/article/details/87723406

我们将该应用实例划分为:common、server和client三个模块。其中,模块common实现了硬件访问服务接口 IFregService,以及Binder本地对象类 BnFregService和Binder代理对象类BpFregService;模块server实现了一个Server进程,它里面包含有一个Service组件FregService;模块client实现了一个Client 进程,他通过一个 BpFregService代理对象去访问运行在Server进程中的Service组件FregService所提供的服务。

目录结构如下:

Android/external/binder

----common
    ---- IFregService.h   : Binder本地对象类BnFregService  Binder代理对象类BpFregService
    ---- IFregService.cpp    : 硬件访问服务接口
    
----server
    ---- FregServer.cpp   : Server进程
    ---- Android.mk
----client
    ---- FregClient.cpp   : Client 进程
    ---- Android.mk
1
2
3
4
5
6
7
8
9
10
common/IFregService.h

#ifndef IFREGSERVICE_H_
#define IFREGSERVICE_H_

#include <utils/RefBase.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>

/* 
* 用来描述Service组件FregService注册到 Service Manager的名称 
*/
#define FREG_SERVICE "hr.ma.FregService"

using namespace android;

/*
* 定义了硬件访问服务接口IFregService,及其两个成员函数,getVal和setVal,分别用来读取和写入虚拟硬件设备freg的寄存器
*  public IInterface : 其实就是硬件访问服务接口,即aidl接口
*/
class IFregService: public IInterface
{
public:

    /* DECLARE_META_INTERFACE(FregService); 声明 IFregService类的元接口
    * 1. 为 FregService 类定义了一个静态成员变量 descriptor,用来描述接口名称,可以通过成员函数getInterfaceDescriptor()来获取。
    * 2. 同时定义了一个静态成员函数 asInterface(),用来讲一个 IBinder对象装换为一个IFregService接口。
    * 3. 最后定义了构造函数和析构函数
    */
    DECLARE_META_INTERFACE(FregService);
    virtual int32_t getVal() = 0;
    virtual void setVal(int32_t val) = 0;
};

/*
* 定义了一个 Binder 本地对象类BnFregService,它实现了模板类 BnInterface的成员函数 onTransact。
*/
class BnFregService: public BnInterface<IFregService>
{
public:

    /*
    * 成员函数 onTransact 是虚函数,它是由 BBinder 的子类,即 Binder本地对象类来实现的,他负责分发与业务相关的进程间通
    * 信请求。事实上,与业务相关的进程间通信请求是由Binder本地对象类的子类,即Service组件来负责处理的。
    */
    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
common/IFregService.cpp

#define LOG_TAG "IFregService"
#include <utils/Log.h>
#include <IFregService.h>

using namespace android;

/*
* 定义了两个进程间通信代码 GET_VAL  SET_VAL,分别对应于 IFregService接口中的 getVal() 和 setVal()
*/
enum
{
    GET_VAL = IBinder::FIRST_CALL_TRANSACTION,
    SET_VAL
};

/*
* 定义BInder 代理对象类  BpFregService,他继承了模块类 BpInterface,并且实现了  IFregService接口
*/
class BpFregService: public BpInterface<IFrefService>
{
public:
    BpFregService(const sp<IBinder>& imp1)
        : BpInterface<IFregService>(imp1)
        {
        
        }
public:
    int32_t getVal()
    {
        /*
        * 首先 将要传递的数据封装在一个 Parcel对象data中
        * 获取 IFregService类的静态成员变量 descriptor,并保存在data中
        */
        Parcel data;
        data.writeInterfaceToken(IFregService::getInterfaceDescriptor());
        
        /*
        * 然后 调用父类 BpRefBase的成员函数 remote 来获得一个 BpBinder代理对象。
        * 最后 调勇这个 BpBinder代理对象的成员函数 transact来请求运行在Service进程中的一个Binder本地
        *         对象执行一个GET_VAL操作,该GET_VAL操作返回结果是一个整数,封装在另一个Parcel对象reply
        *         中,表示虚拟设备freg的寄存器的值。
        *         
        */
        Parcel reply;
        remote()->transact(GET_VAL, data, &reply);
        int32_t val = reply.readInt32();
        return val;
    }
        
    void setVal(int32_t val)
    {
    /*
    * 首先将要传递的数据封装在 一个 Parcel对象data中。
    *  然后使用父类内部的一个BpBinder代理对象的成员函数 transact来请求运行在Server进程中的一个Binder本地对象执行一个 SET_VAL操作,该操作讲一个整数写入到虚拟硬件设备freg的寄存器中。
    */
        Parcel data;
        data.writeInterfaceToken(IFregService::getInterfaceDescriptor());
        data.writeInt32(val);
        Parcel reply;
        remote()->transact(SET_VAL,data, &reply);
    }    
};

/*
* 0. 使用宏 IMPLEMENT_META_INTERFACE 来实现IFregService类的元接口
* 1. 将 IFregService类的静态成员变量 descriptor 设置为  " hr.ma.IFregService " 
* 2. 实现了 IFregService类的构造函数和析构函数,是空函数
* 3. 实现了成员函数 getInterfaceDescriptor() ,用来获取一个IFregService类的描述符,即 descriptor 
* 4. 实现了  asInterface(),用来将一个 IBinder对象装换为一个 IFregService接口。
*/
IMPLEMENT_META_INTERFACE(FregService, "hr.ma.IFregService");

/*
* 定义了 BnFregService 类的成员函数 onTransact,负责将 GET_VALH和SET_VAL进程间通信请求分发给其子类成员函数 getVal和setVal来处理。
*  BnFregService类的子类为FregService,他的成员函数getVal和setVal分别用来读取和写入虚拟硬件设备freg寄存器。
*/
status_t BnFregService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code)
    {
        case GET_VAL:
        {
            CHECK_INTERFACE(IFregService, data, reply);
            int32_t val = getVal();
            reply->writeInt32(val);
            return NO_ERROR;
        }
        case SET_VAL:
        {
            CHECK_INTERFACE(IFregService, data, reply);
            int32_t val = data.readInt32();
            setVal(val);
            return NO_ERRORL
        }
        default:
        {
            return BBinder::onTransact(code, data, reply, flags);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
注意1 宏 DECLARE_META_INTERFACE 和 宏 IMPLEMENT_META_INTERFACE:

宏 DECLARE_META_INTERFACE : frameworks/native/include/binder/IInterface.h

/* DECLARE_META_INTERFACE(FregService);
* 1. 为 FregService 类定义了一个静态成员变量 descriptor,用来描述接口名称,可以通过成员函数getInterfaceDescriptor()来获取。
* 2. 同时定义了一个静态成员函数 asInterface(),用来讲一个 IBinder对象装换为一个IFregService接口。
* 3. 最后定义了构造函数和析构函数
*/
#define DECLARE_META_INTERFACE(INTERFACE)                        
    static const android::String16 descriptor;                     
    static android::sp<I##INTERFACE> asInterface(  const android::sp<android::IBinder>& obj);  
    virtual const android::String16& getInterfaceDescriptor() const;   
    I##INTERFACE();                                                   
    virtual ~I##INTERFACE();                                     


宏 IMPLEMENT_META_INTERFACE

IMPLEMENT_META_INTERFACE(IFregService, "hr.ma.IFregService");
    
/*
* 1. 将 IFregService类的静态成员变量 descriptor 设置为  " hr.ma.IFregService "
* 2. 实现了 IFregService类的构造函数和析构函数,是空函数
* 3. 实现了成员函数 getInterfaceDescriptor() ,用来获取一个IFregService类的描述符,即 descriptor 
* 4. 实现了  asInterface(),用来将一个 IBinder对象装换为一个 IFregService接口。
*/
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
注意2 : CHECK_INTERFACE

BnFregService类的成员函数 onTransact在将 GET_VAL和SET_VAL进程间通信请求发给其子类处理之前,会首先调用宏
 CHECK_INTERFACE 来检查该进程间通信求情的合法性,即检查该请求是否是由 FregService组件的代理对象发送过来的。如果是,那么
 传递过来的Parcel对象data中的第一个数据应该是一个 IFregService接口描述符,即 "hr.ma.IFregService" 。如果不是,那么
 BnFregService类的成员函数 onTransact就会认为这是一个非法的进程间通信请求,就不会继续执行下去。
1
2
3
4
server/FregServer.cpp

#define LOG_TAG "FregServer"
#include <fcntl.h>
#include <utils/Log.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>

#include "../common/IFregService.h"

#define FREG_DEVICE_NAME "/dev/freg"

/*
* 实现了一个 Service组件类FregService,继承自 BnFregService类,并且实现了 IFregService接口。
*/
class FregService: public BnFregService
{
public:

    /*
    * 构造函数中打开设备文件,并将得到的文件描述符保存在成员变量fd中。
    */
    FregService()
    {
        fb = open(FREG_DEVICE_NAME, O_RDWR);
        if(fd == -1){
            LOGE("Failed to open device %s.\n", FREG_DEVICE_NAME );
        }
    }

    /*
    * 析构函数中关闭设备文件
    */
    virtual ~FregService()
    {
        if(fd != -1) {
            close(fd);
        }
    }

public:

    /*
    * FregService类的静态成员函数 instantiate() 负责将一个 FregService组件注册到 Service Manager中,
    *     并且将他的注册名称设置为 “hr.ma.FregService”,这样Client进程就可以通过名称 "hr.ma.FregService"
    *   来获取这个FregService组件的一个代理对象了。
    */
    static void instantiate()
    {
        defaultServiceManager()->addService(String16(FREG_SERVICE), new FregService());
    }

    /*
    * 读取虚拟硬件设备freg寄存器值
    */
    int32_t getVal()
    {
        int32_t val = 0;
        if(fd != -1){
            read(fd, &val, sizeof(val));
        }
        returmn val;
    }

    /*
    * 写入虚拟硬件设备freg寄存器值
    */
    void setVal(int32_t val)
    {
        if(fd != -1){
            write(fd, &val, sizeof(val));
        }
    }

private:
    int fd;    

};

/*
*  定义了 server模块的入口函数main。
*/
int main(int argc, char** argv)
{
    /* 
     * 首先调用 FregService 类的静态成员函数 instantiate将一个 FregService组件注册到 Service Manager中
     */
    FregService::instantiante();

    /* 创建一个线程池
    * 接着调用 进程中的 ProcessState对象的成员函数 startThreadPool来启动一个Binder线程池。
    */
    ProcessState::seif()->startThreadPool();

    /* 加入线程池
    * 调用主线程的 IPCThreadstate对象的成员函数 joinThreadPool将主线程添加到进程的Binder线程中,用来处理
    *     来自Client的进程的通信请求。
    */
    IPCRhreadState::self()->joinThreadPool();
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
server/Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := ../common/IFregService.cpp FregServer.cpp
LOCAL_SHARED_LIBRARIES:= libcutils libutils k\libbinder
LOCAL_MODULE:= FregServer
include $(BUILD_EXECUTABLE)
1
2
3
4
5
6
7
这是 server 模块的编译脚本文件,他会在 out/target/product/gerneric/system/bin 目录中生成一个可执行文件FregServer。

client/FregClient.cpp

#define LOG_TAG "FregClient"
#include <utils/Log.h>
#include <binder/IServiceManager.h>
#include "../common/IFregService.h"

/*
* Client 源码文件,定义了 client模块的入口函数main。
*/
int main()
{
    /*
    * 调用  defaultServiceManager()来获取 Service Manager的一个代理对象
    * 接着调用成员函数 getService()来获取一个名为"hr.ma.FregService" 的 Service组件的一个类型为 BpBinder的代理对象
    *         从 server 模块可知,名为 "hr.ma.FregService" 的 Service组件正好是前面注册的一个FregService组件。
    */
    sp<IBinder> binder = defaultServiceManager()->getService(String16(FREG_SERVICE));
    if(binder == NULL){
        LOGE("failed to get freg server: %s. \n", FREG_SERVICE);
        return -1;
    }

    /*
    * 将前面获得的 BpBinder代理对象封装称为一个 BpFregService代理对象,并且取得他的IFregService接口,保存在变
    *     量service中
    */
    sp<IFregService> service = IFregService::asInterface(binder);
    if(service == NULL){
        LOGE("Failed to get freg service interface.\n");
        return -2;
    }
    
    printf("Read original value from FregService:\n");

    /*
    * 调用IFregService接口service的成员函数 getVal 从运行在另一个进程中的FregService组件获取虚拟硬件设备
    *     freg的寄存器值
    */
    int32_t val = service->getVal();
    printf("%d.\n", val);

    printf("Add value 1 to FregService.\n");
    val +=1;

    /*
    * 调用 IFregService接口service的成员函数setVal来请求运行在另一个进程中的FregService组件将虚拟硬件
    *     设备freg的寄存器值加1.
    */
    service->setVal(val);

    printf("Read the value from FregService again:\n");

    val = service->getVal();
    printf("%d.\n", val);
    return 0;
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
client/Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := ../common/IFregService.cpp FregClient.cpp
LOCAL_SHARED_LIBRARIES :=  libcutils libutils k\libbinder
LOCAL_MODULE := FregClient
include $(BUILD_EXECUTABLE)
1
2
3
4
5
6
7
这是 client 模块的编译脚本文件,他会在 out/target/product/gerneric/system/bin 目录中生成一个可执行文件 FregClient。

模块 common、server、client的源文件以及编译脚本都准备好之后,就可以对他们进行编译打包了。

mmm ./external/binder/server/
mmm ./external/binder/client/
1
2
最后烧录代码之后 进入系统,运行应用程序 FregServer 和 FregClient。

adb shell
cd system/bin
./FregServer &
./FregClient
 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!