android 系统核心机制binder(06)binder C++层 TestClient分析

114 篇文章 93 订阅

该系列文章总纲链接:专题分纲目录 android 系统核心机制 binder


本章关键点总结 & 说明:

这里专注于Binder C++部分的TestClient 分析 部分即可,仅有两个关键点。针对获取服务和使用服务。

一个Client想要得到某个service的信息,需要先和service_manager通信,调用getService获取service信息。(实际上这是对一般常见的非匿名service才采用的策略,对于匿名service是不需要获取服务的)
这里以之前的TestClient客户端测试代码来进行分析,如下所示:

#define LOG_TAG "TestService"
//...
#include "IHelloService.h"
 
using namespace android;
 
/* ./test_client <hello>
 * ./test_client <readfile>
 * ./test_client <hello> <name>
 */
 
int main(int argc, char **argv)
{
	int cnt;
	
	if (argc < 2){
        ALOGI("Usage:\n");
        ALOGI("%s <readfile>\n", argv[0]);
        ALOGI("%s <hello|goodbye>\n", argv[0]);
        ALOGI("%s <hello|goodbye> <name>\n", argv[0]);
        return -1;
	}
 
	/* getService */
	/* 打开驱动, mmap */
	sp<ProcessState> proc(ProcessState::self()); //关键点1
 
	/* 获得BpServiceManager */
	sp<IServiceManager> sm = defaultServiceManager();//关键点2
 
	if (strcmp(argv[1], "hello") == 0)
	{
		sp<IBinder> binder = sm->getService(String16("hello"));//关键点3
		if (binder == 0)
		{
		    ALOGI("can't get hello service\n");
			return -1;
		}
 
		// service肯定是BpHelloServie指针 ,关键点4
		sp<IHelloService> service = interface_cast<IHelloService>(binder);
 
		/* 调用Service的函数 */
		if (argc < 3) {
			service->sayhello();//关键点5
			ALOGI("client call sayhello");
		}
		else {
			cnt = service->sayhello_to(argv[2]);
			ALOGI("client call sayhello_to, cnt = %d", cnt);
		}
	}
	else if (strcmp(argv[1], "readfile") == 0)
	{
 
		//...
	}
	return 0;
}

上面的代码中 对于关键点 ,我们进行了标注,同时,因为 关键点1 、关键点2、关键点4在之前的博文binder TestServer中已经分析过,因此在这里 主要分析 关键点3,因为关键点5 功能点仅为测试。

1 获取服务 流程分析

这里,通过getService获取binder对象,代码实现如下所示:

virtual sp<IBinder> getService(const String16& name) const
    {
        unsigned n;
        for (n = 0; n < 5; n++){
            sp<IBinder> svc = checkService(name);
            if (svc != NULL) return svc;
            ALOGI("Waiting for service %s...\n", String8(name).string());
            sleep(1);
        }
        return NULL;
    }

这里通过checkService来获取binder对象,代码实现如下所示:

 virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }

 这里BpServiceManager发送请求,自后通过reply来获取binder对象。如果要使用返回的BpHelloService获取业务函数,均需要把请求数据打包发送给binder驱动,并且由BpBinder中的handle值找到对应的处理者来处理,处理过程为:@1 通信层收到请求,@2 递交给业务层处理

HelloService中有2个线程在talkWithDriver,假设其中一个线程收到请求消息,会通过之前的executeCommand调用来处理该请求,代码实现如下所示:

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    ...
    switch (cmd) {
    case BR_ERROR:
    ...
    case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            ALOG_ASSERT(result == NO_ERROR,"Not enough command data for brTRANSACTION");
            if (result != NO_ERROR) break;
            
            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(size_t), freeBuffer, this);
            
            const pid_t origPid = mCallingPid;
            const uid_t origUid = mCallingUid;
            
            mCallingPid = tr.sender_pid;
            mCallingUid = tr.sender_euid;
            
            int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
            if (gDisableBackgroundScheduling) {
                if (curPrio > ANDROID_PRIORITY_NORMAL) {                   
                    setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
                }
            } else {
                if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
                    set_sched_policy(mMyThreadId, SP_BACKGROUND);
                }
            }
            
            Parcel reply;
            if (tr.target.ptr) {
                /* BnServiceXXX从BBiner派生,这里的b是实现BnServiceXXX的对象,即直接定位业务层对象 */
                sp<BBinder> b((BBinder*)tr.cookie);
                const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
                if (error < NO_ERROR) reply.setError(error);
            } else {
                /* the_context_object是IPCThreadState中的一个全局变量,
                   可以通过setTheContextObject函数来设置 */
                const status_t error = the_context_object->transact
                    (tr.code, buffer, &reply, tr.flags);
                if (error < NO_ERROR) reply.setError(error);
            }
            
            if ((tr.flags & TF_ONE_WAY) == 0) {
                sendReply(reply, 0);
            }
            
            mCallingPid = origPid;
            mCallingUid = origUid;            
        }
        break;
    ...
    return result;
}

2 onTransact函数

BnHelloService实现了onTransact函数,它根据code调用对应业务逻辑方法。业务逻辑由HelloService来实现的,BnHelloService是继承BBinder的,因此,调用BnHelloService的transact方法,实际上就是调用BBinder的transact方法BBinder的传输代码分析如下所示:

status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    data.setDataPosition(0);
    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            reply->writeInt32(pingBinder());
            break;
        default:
            /* 调用子类的onTransact,这是一个虚函数 */
            err = onTransact(code, data, reply, flags);
            break;
    }
    if (reply != NULL) {
        reply->setDataPosition(0);
    }
    return err;
}

这里的onTransact方法实际上是调用子类的,分析BnHelloService的关键方法onTransact,实现如下所示:

status_t BnHelloService::onTransact( uint32_t code,
                                const Parcel& data,
                                Parcel* reply,
                                uint32_t flags)
{
    switch (code) {
        case HELLO_SVR_CMD_SAYHELLO: {
			sayhello();
			reply->writeInt32(0);  /* no exception */
            return NO_ERROR;
        } break;
		
        case HELLO_SVR_CMD_SAYHELLO_TO: {
			//@1 读取传入参数,并转换
			int32_t policy =  data.readInt32();
			String16 name16_tmp = data.readString16(); /* IHelloService */
			String16 name16 = data.readString16();
			String8 name8(name16);
			//@2 调用对应函数
			int cnt = sayhello_to(name8.string());

			//@3 返回值转换,并发送
			reply->writeInt32(0);  /* no exception */
			reply->writeInt32(cnt);
			
            return NO_ERROR;
        } break;
        //...
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }

这两个流程均为客户端的操作,先是获取servicemanager,通过servicemanager获取HelloService的,通过HelloService获取对应的方法;一个是获取服务的流程,而另一个是使用服务的流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值