该系列文章总纲链接:专题分纲目录 android 系统核心机制 binder
本章关键点总结 & 说明:
这里主要关注 其他部分 匿名service,之类关注的主要是匿名service的 三方面what、why、how即可。
1 匿名Service说明
这里对匿名service(没有在service_manager中注册,但还是基于binder通信的C/S架构)进行特殊说明
(所谓匿名,就是不获取服务,直接通过C/S两端BpXXX和BnXXX来实现,而本质依赖于特定binder)
先查看一个匿名Service的实例,这里以creat方法为例,先看BpMediaPlayerService,代码如下:
virtual sp<IMediaPlayer> create(const sp<IMediaPlayerClient>& client, int audioSessionId) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
data.writeStrongBinder(client->asBinder());
data.writeInt32(audioSessionId);
remote()->transact(CREATE, data, &reply);//将要调用的BnXXX端的方法这一意图发送给binder驱动
return interface_cast<IMediaPlayer>(reply.readStrongBinder());//等待BnXXX端方法的返回值
}
与非匿名service不同的是,可以直接通过binder跳转到BnMediaPlayerService中,代码如下:
status_t BnMediaPlayerService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case CREATE: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IMediaPlayerClient> client = interface_cast<IMediaPlayerClient>(data.readStrongBinder());
int audioSessionId = data.readInt32();
/* IMeidaPlayer类型对象,对于后面的分析很重要 */
sp<IMediaPlayer> player = create(client, audioSessionId);
/* 与驱动交互,特殊binder交互的关键点,将binder类型作为一种特殊类型处理 */
reply->writeStrongBinder(player->asBinder());
return NO_ERROR;
} break;
...
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
接下来可以继续分析继承了BnMediaPlayerService的MediaPlayerService,代码如下:
sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,int audioSessionId)
{
pid_t pid = IPCThreadState::self()->getCallingPid();
int32_t connId = android_atomic_inc(&mNextConnId);
sp<Client> c = new Client(
this, pid, connId, client, audioSessionId,
IPCThreadState::self()->getCallingUid());
ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,
IPCThreadState::self()->getCallingUid());
wp<Client> w = c;
{
Mutex::Autolock lock(mLock);
mClients.add(w);
}
return c;
}
这里的creat方法返回的是一个client对象,是sp<IMediaPlayer>类型,内部类是在BnMediaPlayerService中定义的,代码如下:
class MediaPlayerService : public BnMediaPlayerService
{
class Client;
...
private:
class Client : public BnMediaPlayer {
// IMediaPlayer interface
virtual void disconnect();
...
sp<MediaPlayerBase> createPlayer(player_type playerType);
virtual status_t setDataSource(const sp<IMediaHTTPService> &httpService,const char *url,
const KeyedVector<String8, String8> *headers);
...
private:
friend class MediaPlayerService;
Client( const sp<MediaPlayerService>& service,pid_t pid,int32_t connId,
const sp<IMediaPlayerClient>& client,int audioSessionId,uid_t uid);
Client();
virtual ~Client();
void deletePlayer();
...
}; // Client
...
}
当客户端MeidaPlayerClient调用create方法时,服务端直接返回IMediaPlayer类型对象(该对象是BnMediaPlayerService的内部类),然后将相关信息写入到binder驱动中,之后客户端MeidaPlayerClient就可以直接使用该对象来直接进行跨进程之间的通信了
说明:
- 这里也实现了binder架构的两端:BpMediaPlayer和BnMediaPlayer。
- 在SM中没有IMediaPlayer信息,即无法通过SM获取匹配对应服务的BpBinder进而获取到对应的BBinder的handle值。
- 当reply写到binder驱动中时,驱动为其建立一个全新的handle,等价于在驱动中注册了一项服务。
注意:
- 匿名service的本质就是匿名binder,而匿名binder就是没有向servicemanager提交注册的binder。
- 对于已经建立好Binder通信的Client和server,server可以将一个Binder引用传递给client,client通过引用来访问server。
- 在第一次返回Binder引用时,binder驱动保存了这个Binder实体的各种数据,创建了节点;所以才可以使用匿名binder。
为什么要这样做?
- 匿名Binder一般都是在这种情景中出现的:service有多个实体,在需要使用时,现场创建Binder实体。
- 而一般实名Binder,即是去service manager中注册的service,都只会有一个service实体,即只会new一次。
2 针对reply
这里继续分析其reply的writeStrongBinder的内部实现,代码如下所示:
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
这里实际就是往reply中存入了一个BBinder实体。
继续分析flatten_binder,代码如下所示:
status_t flatten_binder(const sp<ProcessState>& proc,
const sp<IBinder>& binder, Parcel* out)
{
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL) {
IBinder *local = binder->localBinder();//这里我们是由Client强转得到的,所以这里不是空,返回BBinder
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.handle = handle;
obj.cookie = NULL;
} else {
obj.type = BINDER_TYPE_BINDER;//关键,表示当前传入的类型,并将对象本身作为cookie传入。
obj.binder = local->getWeakRefs();
obj.cookie = local;
}
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = NULL;
obj.cookie = NULL;
}
return finish_flatten_binder(binder, obj, out);
}
server可以传递Binder对象的引用给client,流程如下:
- server将Binder对象写入parcel,
- 通过ioctrl和binder驱动通信,binder驱动会对传入的对象进行检查,
- 如果发现传入的type是binder,则会在当前进程(server对应的proc)查找是否有这个节点,
- 如果没有就用传入的Binder实体在user空间指针和cookie(通常是BBinder本身)构造一个新的node,
- 接着得到Binder实体的引用,存放在transaction_data中等待client去获取。
- client拿到Binder的引用后,就可以通过这个BpBinder和server的BBinder进行通信了(和实名注册的Binder是一样的)