Binder系列()——addService——代码分析

本文详细分析了Android Binder通信中的addService过程,涉及Service、ServiceManager和Binder Driver三部分。从MediaPlayerService注册到ServiceManager,到数据在客户端和服务端之间的传输,再到内核层的处理,揭示了Binder的事务处理、数据拷贝和线程交互细节。
摘要由CSDN通过智能技术生成

1 概述

addService的过程涉及三个模块:

  1. Service,service通过调用service manager的addService接口将自己注册到SM,本质上是client;
  2. SM,接收service的注册请求,本质上是server;
  3. Binder Driver,负责CS之间的数据传输和kernel中关键数据结构的建立。

2 Client(MediaPlayerService)

看了网上很多资料都是以MediaPlayerService为例的,本篇也用它来分析。

2.1 MediaPlayerService

frameworks/av/media/mediaserver/main_mediaserver.cpp

int main(int argc __unused, char **argv __unused)   
{                                                   
    signal(SIGPIPE, SIG_IGN);                       
                                                    
    sp<ProcessState> proc(ProcessState::self());      -------------1
    sp<IServiceManager> sm(defaultServiceManager());	-------------2
    ALOGI("ServiceManager: %p", sm.get());          
    InitializeIcuOrDie();                           
    MediaPlayerService::instantiate();              -------------3
    ResourceManagerService::instantiate();          
    registerExtensions();                           
    ProcessState::self()->startThreadPool();        
    IPCThreadState::self()->joinThreadPool();       
}                                                   
  1. 获取该进程对应的ProcessState,进程级别的数据结构,每个进程只有一个,ProcessState相关内容见TOD
  2. 获取ServiceManger对应的BpServiceManager,defaultServiceManager相关内容见TOD
  3. 调用MediaPlayerService的instantiate,如下:

frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp

void MediaPlayerService::instantiate() {                        
    defaultServiceManager()->addService(                        
            String16("media.player"), new MediaPlayerService());  //这行代码信息量很大
}                                                               

随后就调用了BpServiceManager的addService:

 virtual status_t addService(const String16& name, const sp<IBinder>& service,   
         bool allowIsolated)                                                     
 {                                                                               
     Parcel data, reply;                                                         
     data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());    //"android.os.IServiceManager"   
     data.writeString16(name);           //"media.player"                                        
     data.writeStrongBinder(service);   //  MediaPlayerService                                         
     data.writeInt32(allowIsolated ? 1 : 0);                                     
     status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);   //code: ADD_SERVICE_TRANSACTION
     return err == NO_ERROR ? reply.readExceptionCode() : err;                   
 }                                                                                                                                                     

2.1.1 BpServiceManager

需要注意的一点是BpServiceManager的继承关系,如下

							   -->IServiceManager-->IInterface
 BpServiceManager-->BpInterface
							   -->BpRefBase: mRemote(BpBinder)

这种继承方式十分巧妙,一方面保证了BpServiceManager作为interface的功能,可以通过它来进行远程接口调用,另一方面保证了BpServiceManager可以拿到SM的对应的远程binder即BpBinder,基于以上两点实现RPC功能。所以:

  1. BpServiceManager的addService其实是重写了IServiceManager的addService;
  2. BpRefBase中的mRemote保存的是defaultServiceManager()时new的BpBinder,remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply), 调用了BpBinder::transact(…)。

2.1.2 new MediaPlayerService()

作为BpBinder::transact(…)的第二个参数,MediaPlayerService类的继承方式同样也很巧妙,如下:

												      -->IMediaPlayerService
MediaPlayerService-->BnMediaPlayerService->BnInterface
													  -->BBinder

MediaPlayerService整合了Binder数据结构(BBinder)和MediaPlayerService提供的接口类(IMediaPlayerService)。
总结一下BBinder和BpBinder在数据结构上的套路:
BBinder和BpBinder都继承自IBinder,前者是本地对象,后者是远程对象。

  1. 如果是从远程拿到的Binder,那么该数据结构会有远程Service的本地方法和对应的BpBinder,通过这些重写的本地方法再和远程service通信;
  2. 如果是注册服务,那么要将本service的接口类和BBinder整合后,再transact;

一句话,不管是远程还是本地,都既要对应的IBinder数据结构(找到TA),又要接口(使用TA)。

2.2 addService

回到addService,主要做了两件事:

  1. 准备Parcel数据;
  2. 调用BpBinder::transact(…)。

2.2.1 Parcel打包数据

关于Parcel的分析,见binder_context_mgr_node。这里写入的数据为:

data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());    //"android.os.IServiceManager"   
data.writeString16(name);           //"media.player"                                        
data.writeStrongBinder(service);   //  MediaPlayerService
data.writeInt32(allowIsolated ? 1 : 0);  // 0       

数据分两种:

  1. 常规类型的数据,比如SM的token,MediaPlayerService的name,int类型等
  2. 写入IBinder,即传入的MediaPlayerService,此时需要将IBinder类型的数据扁平化,调用writeStrongBinder:
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)  
{                                                           
    return flatten_binder(ProcessState::self(), val, this); 
}                                                           
 status_t flatten_binder(const sp<ProcessState>& /*proc*/,                                 
     const sp<IBinder>& binder, Parcel* out)                                               
 {                                                                                         
     flat_binder_object obj;      											------------------  1                                                   
                                                                                           
     if (IPCThreadState::self()->backgroundSchedulingDisabled()) {                         
         /* minimum priority for all nodes is nice 0 */                                    
         obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;                                         
     } else {                                                                              
         /* minimum priority for all nodes is MAX_NICE(19) */                              
         obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;                                  
     }                                                                                     
                                                                                           
     if (binder != NULL) {                                                                 
         IBinder *local = binder->localBinder();                     ------------------- 2                      
         if (!local) {                                                                     
......                                                  
         } else {                                                                          
             obj.type = BINDER_TYPE_BINDER;                  -------------------3                               
             obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());               
             obj.cookie = <uintptr_t>(local);                              
         }                                                                                 
     } else {                                                                              
......                                                           
     }                                                                                     
                                                                                           
     return finish_flatten_binder(binder, obj, out);     		--------------------4                                  
 }                                                                                         
  1. 定义flat_binder_object,扁平化之后的IBinder就存放在这里;
  2. 注册时的IBinder是BBinder,所以返回不为空;
  3. 本地Binder的类型为BINDER_TYPE_BINDER,binder 保存的是MediaPlayerService的弱引用,cookie保存的是MediaPlayerService的地址。
  4. 将flat_binder_object数据结构写入Parcel变量。
    写入完毕Parcel中的数据应该如下:
    在这里插入图片描述

2.2.1 BpBinder::transact(…)

165         status_t status = IPCThreadState::self()->transact(
166             mHandle, code, data, reply, flags);

mHandle的值为0,是BpBinder在被new的时候的初始化参数,因为是ServiceManager对应的BpBinder,而SM的默认handle就是0。默认flags的值为0,非ONEWAY通信。
接下来调用了IPCThreadState::self()->transact

2.3 IPCThreadState::transact

status_t IPCThreadState::transact(int32_t handle,                                      
                                  uint32_t code, const Parcel& data,                   
                                  Parcel* reply, uint32_t flags)                       
{                                                                                      
    status_t err = data.errorCheck();                                                  
                                                                                       
    flags |= TF_ACCEPT_FDS;                                                            
                                 
    if (err == NO_ERROR) {                                                             
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),              
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");                     
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);   ----------------------1
    }                                                                                  
                                                                                       
    if (err != NO_ERROR) {                                                             
        if (reply) reply->setError(err);                                               
        return (mLastError = err);                                                     
    }                                                                                  
                                                                                       
    if ((flags & TF_ONE_WAY) == 0) {                                                                                                                         
        if (reply) {                                                                   
            err = waitForResponse(reply);               ------------------------2                               
        } else {                                                                       
            Parcel fakeReply; 
            err = waitForResponse(&fakeReply);                            
         }                                                                                                                                                                                    
     } else {                                                              
         err = waitForResponse(NULL, NULL);                                
     }                                                                     
                                                                           
     return err;                                                           
 }                                                                                                                                                       
  1. writeTransactionData对要传输的数据再次封装;
  2. waitForResponse其实不仅等待返回结果,真正与binder driver通信也是在这个函数。

2.3.1 writeTransactionData

IPCThreadState::transact是进入驱动之前应用中的最后一步调用,所以这时候要加上Binder Command(即BC开头的宏)——BC_TRANSACTION。
该函数构造了binder_transaction_data结构体,初始化、保存了binder事物相关的数据,其中也保存了之前的parcel data,然后再次将binder_transaction_data和BC_TRANSACTION写入到IPCThreadState的mOut成员,该成员也是一个Parcel类型数据,在IPCThreadState构造时被初始化。writeTransactionData处理后的数据封装如下图:

在这里插入图片描述

2.3.2 waitForResponse

分两块理解:

  1. talkWithDriver与binder driver通信;
  2. 根据binder driver的返回结果再做操作。
talkWithDriver
 status_t IPCThreadState::talkWithDriver(bool doReceive)                            
 {                                                                                                                                           
     binder_write_read bwr;                                                         
                                                                                    
     // Is the read buffer empty?                                                   
     const bool needRead = mIn.dataPosition() >= mIn.dataSize();                    
                                                                                    
     // We don't want to write anything if we are still reading                     
     // from data left in the input buffer and the caller                           
     // has requested to read the next data.                                        
     const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;        
                                                                                    
     bwr.write_size = outAvail;                                                     
     bwr.write_buffer = (uintptr_t)mOut.data();                                                                
       
      bwr.write_consumed = 0;
      bwr.read_consumed = 0;   
     do {                                                                                                                               
         if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)     --------------2           
             err = NO_ERROR;                                                          
         else                                                                         
             err = -errno;                                                                                                                  
     } while (err == -EINTR);    
           
	   if (err >= NO_ERROR) {                     ------------------3                        
	     if (bwr.write_consumed > 0) {                                  
	         if (bwr.write_consumed < mOut.dataSize())                  
	             mOut.remove(0, bwr.write_consumed);                    
	         else                                                       
	             mOut.setDataSize(0);                                   
	     }                                                              
	     if (bwr.read_consumed > 0) {                                   
	         mIn.setDataSize(bwr.read_consumed);  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值