前面几篇文章,是基于Binder驱动和C/C++层对Binder机制进行了介绍。本文将从Java引用开始,逐步的分析Client是如何与Server进行交互的。本文的例子还是选取MediaPlayer。
目录
1. MediaPlayer的使用示例
2. MediaPlayer示例分析
1. MediaPlayer的使用示例
下面是一个调用MediaPlayer播放音乐(test.mp3)的示例代码。
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
testMediaPlayer();
}
private void testMediaPlayer() {
try {
MediaPlayer mp = new MediaPlayer();
mp.setDataSource("/sdcard/test.mp3");
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.prepare();
mp.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
说明:源码很简单,只需要关注testMediaPlayer()部分即可。首先,新建一个MediaPlayer对象。接着,设置数据源和音频流类型。最后,调用prepare()进行准备之后,再通过start()进行播放。
下面,我们就对该过程进行分析,看看该Binder机制是如何参与其中的。
2. MediaPlayer示例分析
我们将MediaPlayer示例就看作一个MediaPlayer进程,接下来,就看看这个MediaPlayer进程是如何通过Binder机制来和MediaPlayerService通信的。重点需要关注的是mp.setDataSource()的实现。
2.1 MediaPlayer的构造函数
先看看MediaPlayer的构造函数。
public MediaPlayer() {
...
native_setup(new WeakReference<MediaPlayer>(this));
}
private static native final void native_init();
private native final void native_setup(Object mediaplayer_this);
说明:该代码在frameworks/base/media/java/android/media/MediaPlayer.java中。在MediaPlayer中,会调用本地方法native_setup();而且在native_setup()之前,会调用静态方法native_init()进行初始化。下面就看看native_init()和native_setup()各自的代码。
2.2 native_init()和native_setup()的注册信息
static JNINativeMethod gMethods[] = {
...
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
...
{"_start", "()V", (void *)android_media_MediaPlayer_start},
...
{"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
...
};
static int register_android_media_MediaPlayer(JNIEnv *env)
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
...
if (register_android_media_MediaPlayer(env) < 0) {
...
}
...
}
说明:该代码在frameworks/base/media/jni/android_media_MediaPlayer.cpp中。gMethods是JNI本地方法的注册表;在Dalvik虚拟机启动之后,会调用JNI_OnLoad();进而将上面的方法注册到系统中。
这里,我们只需要了解native_init()与android_media_MediaPlayer_native_init()对应,而native_setup()和android_media_MediaPlayer_native_setup()对应即可。
2.3 native_init()的实现
由于native_init()与android_media_MediaPlayer_native_init()对应,下面就看看native_init()的实现。
static void
android_media_MediaPlayer_native_init(JNIEnv *env)
{
jclass clazz;
clazz = env->FindClass("android/media/MediaPlayer");
..
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
...
}
说明:该代码在frameworks/base/media/jni/android_media_MediaPlayer.cpp中。
env->FindClass("android/media/MediaPlayer")会加载Java层的MediaPlayer类;进而将fields.context初始化为MediaPlayer类中的mNativeContext成员。
2.4 native_setup()的实现
由于native_setup()和android_media_MediaPlayer_native_setup()对应,下面就看看native_setup()的实现。
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
sp<MediaPlayer> mp = new MediaPlayer();
...
setMediaPlayer(env, thiz, mp);
}
说明:该函数会新建一个MediaPlayer对象,然后调用setMediaPlayer()来保存该MediaPlayer对象。下面看看setMediaPlayer()是如何保存MediaPlayer对象的。
2.5 setMediaPlayer()
static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
...
env->SetIntField(thiz, fields.context, (int)player.get());
return old;
}
说明:通过SetIntField()会将MediaPlayer对象保存到fields.context中。而在前面的,我们将fields.context初始化为MediaPlayer类(Java层)中的mNativeContext成员。这也就意味着,设置了Java层的MediaPlayer中的mNativeContext成员的值为C++层MediaPlayer对象。
至此,就分析完了MediaPlayer的构造函数。下面继续看Java示例代码中的mp.setDataSource("/sdcard/test.mp3")。
2.6 setDataSource()
public void setDataSource(String path)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(path, null, null);
}
private void setDataSource(String path, String[] keys, String[] values)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
...
final File file = new File(path);
if (file.exists()) {
FileInputStream is = new FileInputStream(file);
FileDescriptor fd = is.getFD();
setDataSource(fd);
is.close();
} else {
...
}
...
}
public void setDataSource(FileDescriptor fd)
throws IOException, IllegalArgumentException, IllegalStateException {
setDataSource(fd, 0, 0x7ffffffffffffffL);
}
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
...
_setDataSource(fd, offset, length);
}
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException;
说明:该代码在frameworks/base/media/java/android/media/MediaPlayer.java中。setDataSource()最终会调用到本地方法setDataSource()。在前面的gMethods本地方法注册表中,将setDataSource()和android_media_MediaPlayer_setDataSourceFD()匹配。下面,看看_setDataSource()的实现。
2.7 android_media_MediaPlayer_setDataSourceFD()
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
...
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
...
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
说明:该代码在frameworks/base/media/jni/android_media_MediaPlayer.cpp中。该函数会先通过getMediaPlayer()获取MediaPlayer对象,然后在执行mp->setDataSource()时会调用MediaPlayer的setDataSource()方法。
2.8 getMediaPlayer()
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
{
...
MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context);
return sp<MediaPlayer>(p);
}
说明:前面在native_setup()中,将fields.context设置为MediaPlayer对象。这里就是返回fields.context中保存的MediaPlayer对象。
2.9 MediaPlayer::setDataSource
status_t MediaPlayer::setDataSource(
const char *url, const KeyedVector<String8, String8> *headers)
{
...
status_t err = BAD_VALUE;
if (url != NULL) {
// 通过getMediaPlayerService()的代理BpMediaPlayerService。
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
// 通过BpMediaPlayerService创建一个IMediaPlayer客户端
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
...
// 保存player
err = attachNewPlayer(player);
}
}
return err;
}
说明:该代码在frameworks/av/media/libmedia/mediaplayer.cpp中。
(01) 它会新建一个service对象,而service是通过getMediaPlayerService()获取到的。getMediaPlayerService()已经在"介绍getService请求"时,详细分析过了。它会返回IMediaPlayerService的代理,即BpMediaPlayerService对象。
(02) 接着,会调用service->create()返回一个IMediaPlayer对象。下面看看这个MediaPlayer进程是如何通过BpMediaPlayerService这个远程代理来获取IMediaPlayer对象的。
2.10 BpMediaPlayerService::create()
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
...
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);
return interface_cast<IMediaPlayer>(reply.readStrongBinder());
}
...
}
说明:该代码在frameworks/av/media/libmedia/IMediaPlayerService.cpp中。
这里无非是CREATE请求数据打包之后发送给Binder驱动,再由Binder驱动转发给MediaPlayerService进程。数据的发送和解析,在前面介绍"addService"和"getService"时已经多次介绍过了;这里就不再展开说明了。
Binder驱动在收到MediaPlayer的数据之后,会将添加一个事务到MediaPlayerService的待处理事务列表中,然后唤醒MediaPlayerService。下面就从MediaPlayerService被唤醒之后开始说明。
2.11 Binder驱动中binder_thread_read()的源码
又回到了熟悉的binder_thread_read()中。
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
void __user *buffer, int size,
signed long *consumed, int non_block)
{
...
if (wait_for_proc_work) {
...
if (non_block) {
...
} else
// 阻塞式的读取,则阻塞等待事务的发生。
ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
} else {
...
}
...
while (1) {
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
if (!list_empty(&thread->todo))
w = list_first_entry(&thread->todo, struct binder_work, entry);
else if (!list_empty(&proc->todo) && wait_for_proc_work)
...
else {
...
}
...
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
t = container_of(w, struct binder_transaction, work);
} break;
...
}
if (!t)
continue;
// t->buffer->target_node是目标节点。
// 这里,MediaPlayer的CREATE请求的目标是MediaPlayerService,因此target_node是MediaPlayerService对应的节点;
if (t->buffer->target_node) {
// 事务目标对应的Binder实体(即,MediaPlayerService对应的Binder实体)
struct binder_node *target_node = t->buffer->target_node;
// Binder实体在用户空间的地址。
// MediaPlayerService的ptr为本地Binder的弱引用,即BBinder的弱引用
tr.target.ptr = target_node->ptr;
// Binder实体在用户空间的其它数据
// MediaPlayerService的cookie为本地Binder本身,即BBinder对象
tr.cookie = target_node->cookie;
t->saved_priority = task_nice(current);
if (t->priority < target_node->min_priority &&
!(t->flags & TF_ONE_WAY))
binder_set_nice(t->priority);
else if (!(t->flags & TF_ONE_WAY) ||
t->saved_priority > target_node->min_priority)
binder_set_nice(target_node->min_priority);
cmd = BR_TRANSACTION;
} else {
...
}
// 交易码,即CREATE
tr.code = t->code;
tr.flags = t->flags;
tr.sender_euid = t->sender_euid;
if (t->from) {
struct task_struct *sender = t->from->proc->tsk;
tr.sender_pid = task_tgid_nr_ns(sender,
current->nsproxy->pid_ns);
} else {
tr.sender_pid = 0;
}
// 数据大小
tr.data_size = t->buffer->data_size;
// 数据中对象的偏移数组的大小(即对象的个数)
tr.offsets_size = t->buffer->offsets_size;
// 数据
tr.data.ptr.buffer = (void *)t->buffer->data +
proc->user_buffer_offset;
// 数据中对象的偏移数组
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
// 将cmd(即BR_TRANSACTION)指令写入到ptr,即传递到用户空间
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
// 将tr数据拷贝到用户空间
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
...
// 删除已处理的事务
list_del(&t->work.entry);
t->buffer->allow_user_free = 1;
// 设置回复信息
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
} else {
...
}
break;
}
done:
// 更新bwr.read_consumed的值
*consumed = ptr - buffer;
...
return 0;
}
说明:MediaPlayerService被唤醒之后,binder_has_thread_work()为true。因为MediaPlayerService的待处理事务队列中有个待处理事务(即,MediaPlayer添加的CREATE请求)。
(01) 进入while循环后,首先取出待处理事务。
(02) 事务的类型是BINDER_WORK_TRANSACTION,得到对应的binder_transaction*类型指针t之后,跳出switch语句。很显然,此时t不为NULL,因此继续往下执行。下面的工作的目的,是将t中的数据转移到tr中(tr是事务交互数据包结构体binder_transaction_data对应的指针),然后将指令和tr数据都拷贝到用户空间,让MediaPlayerService读取后进行处理。
这里,共添加了两个指令到bwr.read_consumed中:BR_NOOP和BR_TRANSACTION。其中,BR_TRANSACTION指令对应的数据中包含了CREATE请求的数据。
接下来,binder_thread_read()返回到binder_ioctl()中;binder_ioctl()将数据拷贝到用户空间之后,便返回到用户空间继续执行。
而在Android Binder机制(八) MediaPlayerService服务的消息循环中介绍过,MediaPlayerService是通过调用IPCThreadState::joinThreadPool()进入消息循环的,而joinThreadPool()又会通过getAndExecuteCommand()调用到talkWithDriver()来和Binder驱动交互的。因此,Binder驱动返回到用户空间之后,会进入talkWithDriver()。
2.12 IPCThreadState::talkWithDriver()
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
...
do {
...
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
...
...
} while (err == -EINTR);
...
if (err >= NO_ERROR) {
// 清空已写的数据
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < (ssize_t)mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
// 设置已读数据
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
...
return NO_ERROR;
}
return err;
}
说明:ioctl()返回之后,talkWithDriver()会清除已经发送的数据。然后,便返回到getAndExecuteCommand()中。
13. IPCThreadState::getAndExecuteCommand()
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
// 和Binder驱动交互
result = talkWithDriver();
if (result >= NO_ERROR) {
...
// 读取mIn中的数据
cmd = mIn.readInt32();
...
// 调用executeCommand()对数据进行处理。
result = executeCommand(cmd);
...
}
return result;
}
说明:getAndExecuteCommand()会取出Binder反馈的指令,然后再调用executeCommand()根据指令进行解析。前面说过,Binder驱动共反馈了BR_NOOP和BR_TRANSACTION两个指令。而BR_NOOP指令什么也不会做。因此,我们直接分析BR_TRANSACTION指令。
14. IPCThreadState::executeCommand()
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
switch (cmd) {
...
case BR_TRANSACTION:
{
binder_transaction_data tr;
result = mIn.read(&tr, sizeof(tr));
...
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);
...
Parcel reply;
...
if (tr.target.ptr) {
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 {
...
}
if ((tr.flags & TF_ONE_WAY) == 0) {
sendReply(reply, 0);
} else {
...
}
...
}
break;
...
}
...
return result;
}
说明:进入BR_TRANSACTION分支后,首先通过mIn.read()读取事务数据。接着,调用ipcSetDataReference()将事务数据解析出来。很显然,tr.target.ptr不为空,它的值是"MediaPlayerService的BBinder的弱引用"。然后,就将tr.cookie转换为BBinder*对象b;而b实际上是MediaPlayerService的本地Binder实例,即BnMediaPlayerService的实例。最终,通过b->transact()进行事务处理。
下面看看BBinder的transact()代码。
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:
err = onTransact(code, data, reply, flags);
break;
}
if (reply != NULL) {
reply->setDataPosition(0);
}
return err;
}
该代码在frameworks/native/libs/binder/Binder.cpp中。此时的code是CREATE,因此,它会调用onTransact()对事务进行处理。而BnMediaPlayerService重写了onTransact()方法;因此会调用到BnMediaPlayerService的onTransact()方法。在Binder机制中也是根据这种方式来实现不同Server的对各自的的请求进行区分处理的:Server的本地Binder实现类,通过覆盖onTransact()方法来处理事务。
下面看看BnMediaPlayerService的onTransact()方法。
15. BnMediaPlayerService::onTransact()
status_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case CREATE: {
...
sp<IMediaPlayerClient> client =
interface_cast<IMediaPlayerClient>(data.readStrongBinder());
int audioSessionId = data.readInt32();
sp<IMediaPlayer> player = create(client, audioSessionId);
reply->writeStrongBinder(player->asBinder());
return NO_ERROR;
} break;
...
}
...
}
说明:该代码在frameworks/av/media/libmedia/IMediaPlayerService.cpp中。
(01) 先通过interface_cast宏获取IMediaPlayerClient对象,该对象是BpMediaPlayerClient实例。BpMediaPlayerClient定义在frameworks/av/media/libmedia/IMediaPlayer.cpp中。
(02) 接着,通过create()创建IMediaPlayerService对象。该create()的实现是在BnMediaPlayerService的子类MediaPlayerService.cpp中,即在frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中实现。在create()中会新建一个Client,并返回。
(03) 最后,将这个Client通过Binder返回给MediaPlayer。
后面的流程就不再多说了。本文的核心是Binder机制,而不是MediaPlayer的框架,让我们了解MediaPlayer进程是如何与MediaPlayerService交互即可!而目前,通过CREATE请求,我们已经知道了MediaPlayer是如何和MediaPlayerService进行事务交互的。后面的内容更多的涉及到MediaPlayerService的框架,它不是本章的重点;感兴趣的读者可以自行研究。