Android Binder 修炼之道(二)Client Server 实例

前面一节,分析了 Binder 系统中的骨架,了解了 Client ServiceManager Server 三者之间的关系,重点针对 ServiceManager 分析了服务的注册过程以及查询过程。本节,将重点放在实现一个简单的 Server 和 Client ,加深对于代码的理解。
首先是 Server:
我们要向 ServiceManager 注册 1 个 calculate 服务,这个服务有两个功能,一个加 1,一个减 1

#ifndef _TEST_SERVER_H
#define _TEST_SERVER_H
#define SVR_CMD_ADD_ONE         0
#define SVR_CMD_REDUCE_ONE      1
#endif // _TEST_SERVER_H
/* Copyright 2008 The Android Open Source Project
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include<stdbool.h>
#include <string.h>
#include <private/android_filesystem_config.h>
#include "binder.h"
#include "test_server.h"
首先来看 main 函数
int main(int argc, char **argv)
{
    int fd;
    struct binder_state *bs;
    uint32_t svcmgr = BINDER_SERVICE_MANAGER;
    uint32_t handle;
    int ret;
    /* 打开 binder 驱动 */
    bs = binder_open(128*1024);
    if (!bs) {
        fprintf(stderr, "failed to open binder driver\n");
        return -1;
    }
    /* 注册服务,最后一个参数不知道干啥用,先随便写 */
    ret = svcmgr_publish(bs, svcmgr, "calculate", (void *)123);
    if (ret) {
        fprintf(stderr, "failed to publish calculate service\n");
        return -1;
    }
    /* 陷入循环,等待 Client 上钩 */
    binder_loop(bs, service_handler);
    return 0;
}
看注册服务函数:
int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
    int status;
    unsigned iodata[512/4];
    struct binder_io msg, reply;
    
    /* 构造 binder_io */
    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);
    
    /* 暂时不知道干啥用的 */
    bio_put_obj(&msg, ptr);
    if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
        return -1;
    status = bio_get_uint32(&reply);
    
    /* 这个也不太清楚 */
    binder_done(bs, &msg, &reply);
    return status;
}
构造 binder_io 的时候先放入了一个 0 ,然后将ServiceManager的名字填充进去,最后把要注册的服务的名字注册进去,然后调用 binder_call 去访问 ServiceManager 的 addService 函数。不搭理那句 bio_put_obj 的话,注册服务的实质就是告诉 ServiceManager 一个服务的名字而已,仅此而已。
注册完两个服务,那么就要等待 Client 上钩,发送数据给我们处理了:
int service_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    /* 根据txn->code知道要调用哪一个函数
     * 如果需要参数, 可以从msg取出
     * 如果要返回结果, 可以把结果放入reply
     */
    uint32_t n,result;
    char name[512];
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int i;
    // Binder 通信的时候都会先塞个0进去,取数据的一方要先把这个0取出来
    strict_policy = bio_get_uint32(msg);
    switch(txn->code) {
    case SVR_CMD_ADD_ONE:
        /* 从msg里取出要处理的数据,并调用处理函数处理 */
        n = bio_get_uint32(msg);
        result = addone(n);
        /* 把结果放入reply */
        bio_put_uint32(reply, result);
        return 0;
    case SVR_CMD_REDUCE_ONE:
        /* 从msg里取出要处理的数据,并调用处理函数处理 */
        n = bio_get_uint32(msg);
        result = reduceone(n);
        /* 把结果放入reply */
        bio_put_uint32(reply, result);
        return 0;
    default:
        fprintf(stderr, "unknown code %d\n", txn->code);
        return -1;
    }
    return 0;
}
这里会产生一个疑问,回复的数据放入 reply 就可以了吗?不需要发送出去?答案是肯定的,你只需要放入 reply 即可了,bind_loop 中会帮你发送出去,看一眼代码踏实:
res = func(bs, txn, &msg, &reply);
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
可以看出,调用我们的处理函数之后,紧接着就是发送回复的数据。
至于这俩函数,不必多说啥
uint32_t addone(uint32_t n)
{
        fprintf(stderr, "i am server,add one %d\n", n);
        return ++n;
}
uint32_t reduceone(uint32_t n)
{
        fprintf(stderr, "i am server,reduce one %d\n", n);
        return --n;
}

下面来看 Client 程序:
明确 Client 要干的事情,我有一个数,我想知道它加1是多少,减1是多少,但是自己不会算,得找个服务商算一算。算之前,得找个中介查查,哪个服务商能提供这个服务。Server 进程相当于服务商,他提供 calculate 服务。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/types.h>
#include<stdbool.h>
#include <string.h>
#include <private/android_filesystem_config.h>
#include "binder.h"
#include "test_server.h"
struct binder_state *g_bs;
uint32_t g_handle;
int main(int argc, char **argv)
{
    int fd;
    struct binder_state *bs;
    uint32_t svcmgr = BINDER_SERVICE_MANAGER;
    uint32_t handle;
    int ret;
    /* 一样的,先打开 Binder 驱动 */
    bs = binder_open(128*1024);
    if (!bs) {
        fprintf(stderr, "failed to open binder driver\n");
        return -1;
    }
    g_bs = bs;
    /* 查询服务,获得服务 handle */
    handle = svcmgr_lookup(bs, svcmgr, "calculate");
    if (!handle) {
        fprintf(stderr, "failed to get addone service\n");
        return -1;
    }
    g_handle = handle;
    /* send data to server */
    if (!strcmp(argv[1], "a")) {
        ret = addone(atoi(argv[2]));
        fprintf(stderr, "get ret of addone= %d\n", ret);
    } else if (!strcmp(argv[1], "r")) {
        ret = reduceone(atoi(argv[2]));
        fprintf(stderr, "get ret of reduceone= %d\n", ret);
    }
    /* Client 使用完就释放服务 */
    binder_release(bs, handle);
    return 0;
}
uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name)
{
    uint32_t handle;
    unsigned iodata[512/4];
    struct binder_io msg, reply;
    /* 构造 binder_io 参数是想要获取 service 的名字 */
    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    bio_put_string16_x(&msg, SVC_MGR_NAME);
    bio_put_string16_x(&msg, name);
    
    /* 查询服务是否存在 */
    if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE))
        return 0;
    
    /* 得到服务的 handle */
    handle = bio_get_ref(&reply);
    
    /* 申请服务,用完 release */
    if (handle)
        binder_acquire(bs, handle);
    binder_done(bs, &msg, &reply);
    return handle;
}
int addone(int n)
{
    unsigned iodata[512/4];
    struct binder_io msg, reply;
    int ret;
    /* 构造binder_io */
    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    /* 放入参数 */
    bio_put_uint32(&msg, n); 
    /* 调用binder_call */
    if (binder_call(g_bs, &msg, &reply, g_handle, SVR_CMD_ADD_ONE))
        return;
    /* 从reply中解析出返回值 */
    ret = bio_get_uint32(&reply);
    binder_done(g_bs, &msg, &reply);
    return ret;
}
int reduceone(int n)
{
    unsigned iodata[512/4];
    struct binder_io msg, reply;
    int ret;
    /* 构造binder_io */
    bio_init(&msg, iodata, sizeof(iodata), 4);
    bio_put_uint32(&msg, 0);  // strict mode header
    /* 放入参数 */
    bio_put_uint32(&msg, n);
    /* 调用binder_call */
    if (binder_call(g_bs, &msg, &reply, g_handle, SVR_CMD_REDUCE_ONE))
        return 0;
    /* 从reply中解析出返回值 */
    ret = bio_get_uint32(&reply);
    binder_done(g_bs, &msg, &reply);
    return ret;
}
编译:
运行:

 

 
不要忘了我们忽略了一个问题,bio_put_obj(&msg, ptr);这个东西是干嘛的?

前面的例子中,我们的 Server 只提供了一个 calculate 服务,如果这个服务商业务比较广,他还能提供查水表服务,那么Server的代码该如何写?
在我们的代码中只有一个服务处理函数,通过binder_loop来处理client的需求,binder_loop(bs, service_handler);
分析 binder_loop 的代码不难发现,这个 Server 进程只要有数据进来,就会调用 service_handler ,至于 client 访问的是哪个服务,该怎样区别对待,就需要用到前面忽略的 ptr 了。
首先,service_handler 作为进程数据的总入口,它有一个参数,binder_transaction_data
struct binder_transaction_data {
        union {
                __u32 handle;        /* target descriptor of command transaction */
                binder_uintptr_t ptr; /* target descriptor of return transaction */
        } target;
        ...
}
我们可以通过 ptr 来区分是哪个服务被调用,从而调用该服务的处理函数,那么ptr是什么东西?是服务处理函数的函数指针。
binder_loop(bs, main_server_handler);
int main_server_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
        int (*handler)(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply);
        handler = (int (*)(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply))txn->target.ptr;
        return handler(bs, txn, msg, reply);
}
写一个 main_server_handler 作为总入口,然后根据其传入的函数指针调用处理函数,也就是 calculate 或 查水表的服务函数
那么这个函数指针 Binder 系统是如何知道的呢?它怎么能知道一个服务对应处理函数的函数指针是多少?这就需要在注册服务的同时,传入这个函数指针。
ret = svcmgr_publish(bs, svcmgr, "calculate", calculate_service_handler);
ret = svcmgr_publish(bs, svcmgr, "chashuibiao", chashuibiao_service_handler);


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值