Android Binder入门指南之Binder Native Service的Java调用流程

Android Binder入门指南之Binder Native Service的Java调用流程

  在前面的篇章中,是基于C/C++层和Binder驱动层对Binder机制进行了介绍。本文将以Java的视角,从Java引用开始,逐步的分析Java Client端是如何与Binder Native Server进行交互的。本文的例子还是选取MediaPlayer。如果单纯的是调用逻辑,不需要这么复杂,因为MediaPlayer牵涉到了许多的业务逻辑,所以Android里面的封装比较复杂,如果是简单的Java层调用Binder Native Service可以参见我的博客Java直接调用Native Binder Service的介绍。

注意:本篇的介绍是基于Android 7.xx平台为基础的。



1.MediaPlayer简单使用实例

下面我们以最简单的MediaPlayer播放音乐为实例,来演示MediaPlayer的使用。

    private void playMusic() {
        try {
            MediaPlayer mp = new MediaPlayer();
            mp.setDataSource("/sdcard/binder.mp3");//看来我是疯了,
            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mp.prepare();
            mp.start();
        } catch (Exception e) {
            e.printStackTrace();
        }   
    } 

源码超级简单,只需要关注testMediaPlayer()部分即可。首先,新建一个MediaPlayer对象。接着,设置数据源和音频流类型。最后,调用prepare()进行准备之后,再通过start()进行播放。当然这个只是演示代码,在实际的MediaPlayer应用开发中,不止如此。

下面,我们就对该过程进行分析,看看该Binder机制是如何参与其中的。





2.MediaPlayer客户端进程调用流程分析

我们将该MediaPlayer示例App看作一个MediaPlayer客户端进程,接下来,就看看这个MediaPlayer客户端进程是如何通过Binder机制来和MediaPlayerService通信的。重点需要关注的是mp.setDataSource()的实现。


2.1 MediaPlayer的构造函数

Java世界里,对一个类的分析一般从构造函数开始,当然我们的MediaPlayer也不例外。

    private static native final void native_init();
    private native final void native_setup(Object mediaplayer_this);
    private native final void native_finalize();
    private long mNativeContext; // accessed by native methods
    
    public MediaPlayer() {
		...
        native_setup(new WeakReference<MediaPlayer>(this));
    }

    static {
        System.loadLibrary("media_jni");//加载so库
        native_init();//初始化
    }

该代码在frameworks/base/media/java/android/media/MediaPlayer.java中。在MediaPlayer的默认构造方法中,会调用本地方法native_setup()。而且在native_setup()之前,会调用静态方法加载so库并且调用native_init()进行初始化。下面就看看native_init()和native_setup()各自的代码。


2.2 native_init()和native_setup()的注册信息

static const 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 */)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
    assert(env != NULL);
	...
    if (register_android_media_MediaPlayer(env) < 0) {
        ALOGE("ERROR: MediaPlayer native registration failed\n");
        goto bail;
    }
    ...
    /* success -- return valid version number */
    result = JNI_VERSION_1_4;

bail:
    return result;

代码在frameworks/base/media/jni/android_media_MediaPlayer.cpp中,这里教读者一个比较快的查找Java类对应的JNI文件的文件名的快捷方式,譬如我们这里的MediaPlayer的类的全程是android.media.MediaPlayer,然后将.切换成_可以得到android_media_MediaPlayer然后再在后面加上.cpp就是对应的文件名了。这个查找方法在Android中很管用,不信的话读者可以多找几个类试试。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");
    if (clazz == NULL) {
        return;
    }
    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    if (fields.context == NULL) {
        return;
    }
    ...
}

该代码在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 = NULL;
   	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)
{
    Mutex::Autolock l(sLock);
    sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    if (player.get()) {
        player->incStrong((void*)setMediaPlayer);
    }
    if (old != 0) {
        old->decStrong((void*)setMediaPlayer);
    }
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}

通过SetIntField()会将MediaPlayer对象保存到fields.context中。而在前面的,我们将fields.context初始化为MediaPlayer类(Java层)中的mNativeContext成员。这也就意味着,设置了Java层的MediaPlayer中的mNativeContext成员的值为C++层MediaPlayer对象。
至此,就分析完了MediaPlayer的构造函数。下面继续看MediaPlayer客户端进程示例代码中的mp.setDataSource("/sdcard/binder.mp3")。


2.6 setDataSource()

    public void setDataSource(String path)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        setDataSource(path, null, null);
    }

    public void setDataSource(String path, Map<String, String> headers)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
    {
		...
        setDataSource(path, keys, values);
    }
    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)
{
    Mutex::Autolock l(sLock);
    MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
    return sp<MediaPlayer>(p);
}

前面在native_setup()中,将fields.context设置为MediaPlayer对象。这里就是返回fields.context中保存的MediaPlayer对象。


2.9 MediaPlayer::setDataSource

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
    status_t err = UNKNOWN_ERROR;
    //通过getMediaPlayerService()获取MediaPlayerService的代理BpMediaPlayerService。
    const sp<IMediaPlayerService> service(getMediaPlayerService());
    if (service != 0) {
    	// 通过BpMediaPlayerService创建一个IMediaPlayer客户端
        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
            (NO_ERROR != player->setDataSource(fd, offset, length))) {
            player.clear();
        }
        err = attachNewPlayer(player);
    }
    return err;
}

该代码在frameworks/av/media/libmedia/mediaplayer.cpp中,该代码主要做了如下几个事情:
(1) 它会新建一个service对象,而service是通过getMediaPlayerService()获取到的。getMediaPlayerService()已经在"getService请求"时,详细分析过了。它会返回IMediaPlayerService的代理,即BpMediaPlayerService对象。
(2) 接着,会调用service->create()返回一个IMediaPlayer对象。下面看看这个MediaPlayer进程是如何通过BpMediaPlayerService这个远程代理来获取IMediaPlayer对象的。


2.10 BpMediaPlayerService::create()

    virtual sp<IMediaPlayer> create(
            const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeStrongBinder(IInterface::asBinder(client));
        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,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
	...
	if (wait_for_proc_work) {
		...
		if (non_block) {
			...
		} else
			//阻塞式读取,阻塞等待事务的发生,此时会被唤醒
			ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
	} else {
		...
	}
	...
	while (1) {
		uint32_t cmd;
		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)
			w = list_first_entry(&proc->todo, struct binder_work, entry);
		else {
			...
		}

		...

		switch (w->type) {
		case BINDER_WORK_TRANSACTION: {
			t = container_of(w, struct binder_transaction, work);
		} break;
			...
		}

		if (!t)
			continue;

		//此时t-buffer-target_node是目标节点。
		//这里,ediaPlayer的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 = from_kuid(current_user_ns(), t->sender_euid);

		if (t->from) {
			struct task_struct *sender = t->from->proc->tsk;
			tr.sender_pid = task_tgid_nr_ns(sender,
							task_active_pid_ns(current));
		} else {
			tr.sender_pid = 0;
		}

		//数据大小
		tr.data_size = t->buffer->data_size;
		//数据中对象的偏移数组的大小(即对象的个数)
		tr.offsets_size = t->buffer->offsets_size;
		//数据
		tr.data.ptr.buffer = (binder_uintptr_t)(
					(uintptr_t)t->buffer->data +
					proc->user_buffer_offset);
		//数据中对象的偏移数组
		tr.data.ptr.offsets = tr.data.ptr.buffer +
					ALIGN(t->buffer->data_size,
					    sizeof(void *));

		//将cmd指令写入到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)) {
			...
		} else {
			t->buffer->transaction = NULL;
			kfree(t);
			binder_stats_deleted(BINDER_STAT_TRANSACTION);
		}
		break;
	}

done:
	//更新bwr.read_comsumed的值
	*consumed = ptr - buffer;
	...
	return 0;
}

MediaPlayerService被唤醒之后,binder_has_thread_work()为true。因为MediaPlayerService的待处理事务队列中有个待处理事务(即,MediaPlayer添加的CREATE请求)。
(1) 进入while循环后,首先取出待处理事务。
(2) 事务的类型是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机制(九) Binder服务的消息循环中介绍过,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;
}

此时MediaPlayerService从Binder驱动中返回后,继续处理,talkWithDriver()会清除已经发送的数据。然后,便返回到getAndExecuteCommand()中。


2.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指令。


2.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()方法。


2.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中。
(1) 先通过interface_cast宏获取IMediaPlayerClient对象,该对象是BpMediaPlayerClient实例。BpMediaPlayerClient定义在frameworks/av/media/libmedia/IMediaPlayer.cpp中。
(2) 接着,通过create()创建IMediaPlayerService对象。该create()的实现是在BnMediaPlayerService的子类MediaPlayerService.cpp中,即在frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中实现。在create()中会新建一个Client,并返回。
(3) 最后,将这个Client通过Binder返回给MediaPlayer。

后面的流程就不再多说了。本文的核心是Binder机制,而不是MediaPlayer的框架,让我们了解MediaPlayer进程是如何与MediaPlayerService交互即可!而目前,通过CREATE请求,我们已经知道了MediaPlayer是如何和MediaPlayerService进行事务交互的。后面的内容更多的涉及到MediaPlayerService的框架,它不是本章的重点;感兴趣的读者可以自行研究。



写在最后

  在前面的章节,主要讲解了C/C++层Binder服务的添加和获取,本篇主要讲解了Java层怎么获取Native Binder Service,在后续的篇章里面会继续讲解怎么通过Java层Binder服务的添加和获取。这个的难度不是很大,因为绝大部分的还是在底层实现的,然后通过JNI再封装一层罢了。

发布了89 篇原创文章 · 获赞 92 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/tkwxty/article/details/103564202