binder

关于android下的binder IPC机制,网上已经有不少人分析过了,我总是嫌看起 来有些雾里看花,所以一咬牙自己看一遍吧。

首先,众所周知,IPC也即进程间通信,Inter Process Communication,它的形 式非常多种多样,比大家所熟知的那几种要多得多。在我看来,一些普遍不被认 为是IPC的机制,其实也可以认为是一种IPC,只不过可能原始一点,或者不够健 壮。

比如文件,两个进程完全可以通过IPC来进行通信嘛。进程A先往文件 /tmp/ab-ipc-file写一句话,“今晚打老虎”,然后sleep 10秒,进程B读此文件, 回复“好的”(把“好的”写到这个文件里去,覆盖了原来的内容),然后退出,然 后A醒来,重新读一下这个文件,获得了B传给它的信息。这样,它们就完成了一 次双向的IPC。

如果把进程间通信跟人类之间的沟通做一个类比的话,能更深的理解的确有很多 种机制均可以被打破常规的认为是IPC。沟通指的是信息的交换。而信息之所以 成为信息,是因为它可以被交换。可以被产生,被观察到,被消化掉。一个只被 产生出来,却从来没有被观察、被消化过的信息,是无法想像的。是不存在的。 比如一个女人,生了一个孩子,这件事本身好像不存在信息交换,孩子刚出生, 还不会跟妈妈交换信息嘛。可如果把范围划得广一点,就会发现在生孩子的过程 中,发生了很多信息交换,比如孩子它爸不停地在妈妈身边喊,“加油!加油!”。 等等等等。产房外焦急等待的长辈在不停地poll产房里的情况…

可惜,把范围定得这么广的话我们就没法继续讨论下去了。所以我还是稍微收一 下。

Binder IPC机制跟其它常见的IPC机制一样,里面包含一些基本的元素:

  • 参与者

    您肯定听说过,signal也是一种IPC机制,一个基本上是单向的IPC机制。进程 A通过Kill命令/系统调用向进程B发一个信号,发送成功的话进程B可能会做出 一些动作来响应这个信号。这里的A和B就是参与者。但有时候进程A是不存在 的,比如当一个子进程退出的时候,父进程会收到一个sigchld的信号,但这 个信号是sigchld是谁发给父进程的呢?刚刚退出的子进程发的吗?可它没有 调过kill系统调用啊?答案是操作系统发的。所以进程间通讯真的不是那么绝 对的。

    有时候进程间通讯可以是多方的,比如管道,大家可能会认为肯定只能是两方 参与,但其实弄4个进程出来通过一个管道通信也不是什么难事:

    (echo hello; echo world) | ( dd bs=1 count=6 && perl -npe '' )
    

    这些参与者参与到某种通信机制的方式随机制的不同而不同。比如signal机制, 我可以写这样的一段淘气的脚本,不停地随机地给系统中的进程发 sigkill(其中random是一个附助的生成随机整数的脚本):

    while true; do kill -9 $(random); done
    

    而上面那一段4方管道,显然需要有一个父进程(bash)帮助把这个管道创建出 来,把子进程创建出来,把输入输出配置好,再把相应的程序执行起来。

    而Binder机制里,系统中所有的进程都可以参与其中,只要它们打开了 /dev/binder这个设备节点就可以。但其中有一个比较特殊一点,一般是第一 个打开/dev/binder节点并成为context manager的那个进程。

  • 角色

    对,IPC中的各方是可以有不同的角色的。比如signal,有一个杀的,有一 个被杀的(kill:-))。

    而在Binder通讯机制里,有一个角色比较特殊,那就是context manager。 这个在kernel里是叫context manager,其实在user space里是叫service manager。它允许系统里其它进程向它注册,比如这个进程向service manager注册:“我的进程号是NNNN,我的名字叫media server”,以后别的 进程想进行多媒体相关的调用的话,就会向service manager查询,“media server”的进程是哪个?

    所以除了这个service manager之外,可以有很多其他的server,其实 service manager自身也是一个server,只不过是最特殊的那个罢了。其他 所有server都要向它来注册。

大体看过Binder的一些特点之后,我们来场景分析一下Binder相关的一些细节。 您可以别看了,我写得太乱了…

1 Binder是一种面向对象的IPC机制

作为一种很牛逼的IPC机制,Binder想要作到让跨进程间通讯的远程调用,使用 起来感觉就像是进程内的函数调用一样。这个就有难度了。让我们来看看它是怎 么实现的。

首先,在远程server端会实现一套接口(Interface)。啊,算了,让我们先来 说一说如果没有binder机制,我们会怎么样来实现远程通讯吧。

我最熟悉的远程通讯就是tcp/ip了,这个通讯机制非常的通用,我们就不提那些 大家都熟知的http/ftp/ssh/telnet等协议了。

我曾经实现过一个手机测试命令工具箱,tcmd,采用了server/client机制实现。 首先一个server在一端监听某tcp端口,比如54321,客户端会给它发一个命令, 比如wifi-off,那么服务端读到这个命令之后,会以这样的代码去做相应的事情:

    read(socket, cmd ...
    if (strcmp(cmd, "wifi-on") == 0) {
        wifi_on();
    } else if (strcmp(cmd, "wifi-off") == 0) {
        wifi_off();
    } else if ...

void wifi_off()
{
    write 0 /sys/wifi/power
}

也就是说,服务端系统会根据读到不同的命令去封装不同的函数。

比较妙的是,在客户端系统会去实现类似的逻辑,只不过是反过来,像一面镜子,它是往socket里写:

void client_wifi_on()
{
    socket = open_socket("localhost", 54321);
    write(socket, "wifi-on"...
    close(socket);
}

void client_wifi_off()
{
    socket = open_socket("localhost", 54321);
    write(socket, "wifi-off"...
    close(socket);
}

这里你也看到了,我们可以像模板一样,生成很多的函数,每个函数做一个命令。 当然,没有什么能阴止你使用跟server端一样的名字,比如你的 client_wifi_off 完全也可以叫做 wifi_off ,这样做是有好处的,使两个本来非 常相关的函数变得更紧密了,但也有坏处,阅读代码的时候常常不知道该看哪个。 其实看多了很快会明白,所有的client端的实现都是个模板而已(boilerplate)。

然而我也非常喜欢这样的client实现:

void test_cmd(const char *cmd)
{
    socket = open_socket("localhost", 54321);
    write(socket, cmd...
    close(socket);
}

然后再用到 client_wifi_on() 的地方就代以 test_cmd("wifi-on") ,在用到 wifi_off() 的地方就代以 test_cmd("wifi-off") ,等等。这样做的好处是少了很 多函数和很多boilerplate代码(每个命令都要open/close socket!)。

当然,你也可以再在 test_cmd 的基础上把 wifi_on/wifi_off 给包装出来:

void wifi_on()
{
    test_cmd("wifi-on");
}

太棒了,这就跟我们最终要看的binder机制有点像了。好,我们接着来看一下 binder机制是怎么做的。

1.1 接口、继承树

Binder机制从最底层讲,只是数据的封装、传送、解析而已,所以它并不是只有 特定语言可以使用的,至少在Android系统里C/C++/Java都可以使用Binder。

但它最重要的优点之一,应该是它与面向对象编程之间比较友好的关系。我们就 先来看看Java里面是怎么使用Binder机制的。

题外话:我在用ajoke.el/beagrep/ctags/global等开源软件阅读源代码,以下 会有一些这些工具的运行结果。

以MountService.java为例,它有一个shutdown函数,我们看看它是哪个binder 的:

-*- mode: compilation; default-directory: "~/src/android/frameworks/base/services/java/com/android/server/" -*-
Compilation started at Sat Jun 21 21:24:30

ajoke-get-hierarchy.pl com.android.server.MountService -m shutdown
make: Entering directory `/home/bhj/src/android'

=> class com.android.server.MountService at frameworks/base/services/java/com/android/server/MountService.java line 103.
      public void shutdown(final IMountShutdownObserver observer){
   => class android.os.storage.IMountService.Stub at frameworks/base/core/java/android/os/storage/IMountService.java line 37.
      => class android.os.Binder at frameworks/base/core/java/android/os/Binder.java line 43.
         => interface android.os.IBinder at frameworks/base/core/java/android/os/IBinder.java line 86.
         => class java.lang.Object at libcore/luni/src/main/java/java/lang/Object.java line 131.
      => interface android.os.storage.IMountService at frameworks/base/core/java/android/os/storage/IMountService.java line 35.
            public void shutdown(IMountShutdownObserver observer)throws RemoteException;
         => interface android.os.IInterface at frameworks/base/core/java/android/os/IInterface.java line 23.
   => interface com.android.server.INativeDaemonConnectorCallbacks at frameworks/base/services/java/com/android/server/INativeDaemonConnectorCallbacks.java line 20.
   => interface com.android.server.Watchdog.Monitor at frameworks/base/services/java/com/android/server/Watchdog.java line 170.

Compilation finished at Sat Jun 21 21:24:31

哦,对了,你看到的上面这段代码就是它在我的Emacs编辑器里显示的样子,为 了让它正确的代码高亮,我还颇费了一番功夫呢!首先,上面的代码是 compilation-mode,这个在Emacs里是一种read-only的模式,然而org-html-fontify-code的代码是这样写的:

(setq code (with-temp-buffer
             ;; Switch to language-specific mode.
             (funcall lang-mode)
             (insert code)
             ;; Fontify buffer.
             (font-lock-fontify-buffer)

可见,它先设置了mode (funcall lang-mode) 为compilation-mode,然后再 插入code (insert code) ,这就出问题了,compilation-mode是只读的,不 允许插入。为了绕过这个问题,我定义了一种新的compout-mode,很简单,就是可写的compilation-mode:

(defun compout-mode ()
  "compilation mode, which is not buffer readonly for org export"
  (interactive)
  (compilation-mode)
  (setq buffer-read-only nil))

另外,做成这样之后,还是有问题,上面的代码输出到html之后没有全部高亮, 经查,应该是compilation-mode对temp-buffer有歧视,在temp-buffer里不会最 努力地高亮(见上面的 with-temp-buffer )。

with-temp-buffer 的定义是这样的:

(defmacro with-temp-buffer (&rest body)
  "Create a temporary buffer, and evaluate BODY there like `progn'.
See also `with-temp-file' and `with-output-to-string'."
  (declare (indent 0) (debug t))
  (let ((temp-buffer (make-symbol "temp-buffer")))
    `(let ((,temp-buffer (generate-new-buffer " *temp*")))
       ;; FIXME: kill-buffer can change current-buffer in some odd cases.
       (with-current-buffer ,temp-buffer
         (unwind-protect
             (progn ,@body)
           (and (buffer-name ,temp-buffer)
                (kill-buffer ,temp-buffer)))))))

问题出在 (generate-new-buffer " *temp*") 这个语句,buffer名字不能叫 *temp* ,所以我定义了一个advice来绕开这个问题:

(defun org-html-fontify-code-compout (orig-fun &rest args)
  "Make compilation output htmlized."
  (if (string= (cadr args) "compout")
      (flet ((generate-new-buffer (name)
                                  (when (string= name " *temp*")
                                    (setq name "temp-ox"))
                                  (get-buffer-create (generate-new-buffer-name name))))
        (apply orig-fun args))
    (apply orig-fun args)))

(eval-after-load 'ox-html
  '(advice-add 'org-html-fontify-code :around #'org-html-fontify-code-compout))

当第二个参数 (cadr args) 为 "compout" 时,让generate-new-buffer的定 义发生变化,如果其参数名为 *temp* ,则改成 temp-ox 。

好了,从上面我用 ajoke-get-hierarchy 搞出来的继承树来看, shutdown 函数最早是在IMountService里定义的:

=> interface android.os.storage.IMountService at frameworks/base/core/java/android/os/storage/IMountService.java line 35.
      public void shutdown(IMountShutdownObserver observer)throws RemoteException;
-*- mode: grep; default-directory: "~/src/android/frameworks/base/core/java/android/os/" -*-
Grep started at Fri Jul 25 10:02:01

grep-func-call -e "IMountService.*asInterface" --nc -a -v /tests/


Entering directory `/home/bhj/src/android/frameworks/base/core/java/android/app'
ContextImpl.java:2172: <=     private File[] ensureDirsExistOrFilter(File[] dirs) {
ContextImpl.java:2182: =>                         final IMountService mount = IMountService.Stub.asInterface(
Entering directory `/home/bhj/src/android/frameworks/base/core/java/android/os'
Environment.java:74: <=     private static StorageVolume getPrimaryVolume() {
Environment.java:83: =>                         IMountService mountService = IMountService.Stub.asInterface(ServiceManager
Environment.java:707: <=     public static String getStorageState(File path) {
Environment.java:717: =>             final IMountService mountService = IMountService.Stub.asInterface(
Entering directory `/home/bhj/src/android/frameworks/base/core/java/android/os/storage'
IMountService.java:836: <>         public static IMountService asInterface(IBinder obj) {
IMountService.java:857: <=         public boolean onTransact(int code, Parcel data, Parcel reply,
IMountService.java:867: =>                     listener = IMountServiceListener.Stub.asInterface(data.readStrongBinder());
IMountService.java:875: =>                     listener = IMountServiceListener.Stub.asInterface(data.readStrongBinder());
Entering directory `/home/bhj/src/android/frameworks/base/core/java/android/os/storage'
StorageManager.java:315: <=     public StorageManager(ContentResolver resolver, Looper tgtLooper) throws RemoteException {
StorageManager.java:318: =>         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
Entering directory `/home/bhj/src/android/frameworks/base/core/java/com/android/internal/content'
PackageHelper.java:61: <=     public static IMountService getMountService() throws RemoteException {
PackageHelper.java:64: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/frameworks/base/core/java/com/android/internal/os/storage'
ExternalStorageFormatter.java:319: <=     IMountService getMountService() {
ExternalStorageFormatter.java:323: =>                 mMountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/frameworks/base/core/java/com/android/internal/widget'
LockPatternUtils.java:632: <=     private void updateEncryptionPassword(String password) {
LockPatternUtils.java:645: =>         IMountService mountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/frameworks/base/packages/BackupRestoreConfirmation/src/com/android/backupconfirm'
BackupRestoreConfirmation.java:131: <=     public void onCreate(Bundle icicle) {
BackupRestoreConfirmation.java:159: =>         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
Entering directory `/home/bhj/src/android/frameworks/base/packages/SystemUI/src/com/android/systemui/usb'
UsbStorageActivity.java:202: <=     private IMountService getMountService() {
UsbStorageActivity.java:205: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/frameworks/base/services/java/com/android/server'
BackupManagerService.java:773: <=     public BackupManagerService(Context context) {
BackupManagerService.java:781: =>         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
Entering directory `/home/bhj/src/android/frameworks/base/services/java/com/android/server/power'
ShutdownThread.java:295: <=     public void run() {
ShutdownThread.java:374: =>                 final IMountService mount = IMountService.Stub.asInterface(
Entering directory `/home/bhj/src/android/packages/apps/SettingsSmartisan/src/com/android/settings'
CryptKeeperConfirm.java:41: <=         public void onCreate(Bundle savedInstanceState) {
CryptKeeperConfirm.java:72: =>                     IMountService mountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/SettingsSmartisan/src/com/android/settings'
CryptKeeper.java:614: <=     private IMountService getMountService() {
CryptKeeper.java:617: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/SettingsSmartisan/src/com/android/settings/deviceinfo'
Memory.java:214: <=     private synchronized IMountService getMountService() {
Memory.java:218: =>                mMountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/Settings/src/com/android/settings'
CryptKeeperConfirm.java:41: <=         public void onCreate(Bundle savedInstanceState) {
CryptKeeperConfirm.java:72: =>                     IMountService mountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/Settings/src/com/android/settings'
CryptKeeper.java:614: <=     private IMountService getMountService() {
CryptKeeper.java:617: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/Settings/src/com/android/settings/deviceinfo'
Memory.java:203: <=     private synchronized IMountService getMountService() {
Memory.java:207: =>                mMountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/SystemUISmartisan/src/com/smartisanos/systemui/usb'
UsbStorageActivity.java:204: <=     private IMountService getMountService() {
UsbStorageActivity.java:207: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/vendor/qcom/proprietary/QualcommSettings/src/com/qualcomm/qualcommsettings'
QualcommSettings.java:687: <=    private void updateUsbMassStorageStatus() {
QualcommSettings.java:689: =>         IMountService mountService = IMountService.Stub.asInterface(ServiceManager

Grep finished (matches found) at Fri Jul 25 10:02:02

啊,还是看一看shutdown在I?MountService里都有谁定义的:

-*- mode: grep; default-directory: "~/src/android/frameworks/base/core/java/android/os/storage/" -*-
Grep started at Fri Jul 25 10:17:13

grep-gtags -e "shutdown" -p MountServ
Found total 4/1728 definitions: Entering directory `/home/bhj/src/android'
frameworks/base/core/java/android/os/storage/IMountService.java:451: method: <android.os.storage.IMountService.Stub.Proxy.shutdown> : public void shutdown(IMountShutdownObserver observer) throws RemoteException {
frameworks/base/core/java/android/os/storage/IMountService.java:1310: method: <android.os.storage.IMountService.shutdown> : public void shutdown(IMountShutdownObserver observer) throws RemoteException;
frameworks/base/services/java/com/android/server/MountService.java:1423: method: <com.android.server.MountService.shutdown> : public void shutdown(final IMountShutdownObserver observer) {
frameworks/base/libs/storage/IMountService.cpp:403: function: <android::BpMountService::shutdown> : void shutdown(const sp<IMountShutdownObserver>& observer) {
    403:     void shutdown(const sp<IMountShutdownObserver>& observer)
    404:     {
    405:         Parcel data, reply;
    406:         data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
    407:         data.writeStrongBinder(observer->asBinder());
    408:         if (remote()->transact(TRANSACTION_shutdown, data, &reply) != NO_ERROR) {
    409:             ALOGD("shutdown could not contact remote\n");
    410:             return;
    411:         }
    412:         int32_t err = reply.readExceptionCode();
    413:         if (err < 0) {
    ...

Grep finished (matches found) at Fri Jul 25 10:17:14

可见有一个interface里声明了这个函数的原型android.os.storage.IMountService.shutdown,然后有一个stub.proxy的实现: android.os.storage.IMountService.Stub.Proxy.shutdown,但它的实现只是进行了binder的系统调用而已:

public void shutdown(IMountShutdownObserver observer)
        throws RemoteException {
    Parcel _data = Parcel.obtain();
    Parcel _reply = Parcel.obtain();
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeStrongBinder((observer != null ? observer.asBinder() : null));
        mRemote.transact(Stub.TRANSACTION_shutdown, _data, _reply, 0);
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

然后 android.os.storage.IMountService.Stub 实现了这个Interface,但 android.server.MountService 又扩展了这个 Stub:

    public static abstract class Stub extends Binder implements IMountService {}
class MountService extends IMountService.Stub
        implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {}

然后注意上面proxy里的实现,发了一个 TRANSACTION_shutdown 的transact,先搜一下 TRANSACTION_shutdown 吧:

-*- mode: grep; default-directory: "~/system-config/doc/baohaojun/blog/2014/06/04/" -*-
Grep started at Fri Jul 25 10:27:07

beagrep -e "TRANSACTION_shutdown" -d ~/src/android
/home/bhj/src/android/frameworks/base/core/java/android/os/storage/IMountService.java:458:                    mRemote.transact(Stub.TRANSACTION_shutdown, _data, _reply, 0);
/home/bhj/src/android/frameworks/base/core/java/android/os/storage/IMountService.java:800:        static final int TRANSACTION_shutdown = IBinder.FIRST_CALL_TRANSACTION + 19;
/home/bhj/src/android/frameworks/base/core/java/android/os/storage/IMountService.java:1048:                case TRANSACTION_shutdown: {
/home/bhj/src/android/frameworks/base/libs/storage/IMountService.cpp:44:    TRANSACTION_shutdown,
/home/bhj/src/android/frameworks/base/libs/storage/IMountService.cpp:408:        if (remote()->transact(TRANSACTION_shutdown, data, &reply) != NO_ERROR) {

Grep finished (matches found) at Fri Jul 25 10:27:07

.cpp的那个我们就不看了,完全在拷贝.java这边的代码。当然也可能是.java在拷贝.cpp的。

处理的地方是在IMountService.java:1048:行,这个是Stub里定义的,也就是说 Stub只实现了针对每个 TRANSACTION_xxx 应该怎么调用由子类(MountService) 实现的函数,它自己是一个也没有实现过的。

再看一下Stub自己一共定义了多少个函数吧:

-*- mode: grep; default-directory: "~/src/android/frameworks/base/core/java/android/os/storage/" -*-
Grep started at Fri Jul 25 14:44:33

grep-gtags -e "android.os.storage.IMountService.Stub..*" -t method |grep -v Proxy
Found total 42/38 definitions: Entering directory `/home/bhj/src/android'
frameworks/base/core/java/android/os/storage/IMountService.java:836: method: <android-os-storage-IMountService-Stub-asInterface> : public static IMountService asInterface(IBinder obj) {
frameworks/base/core/java/android/os/storage/IMountService.java:848: method: <android-os-storage-IMountService-Stub-Stub> : public Stub() {
frameworks/base/core/java/android/os/storage/IMountService.java:852: method: <android-os-storage-IMountService-Stub-asBinder> : public IBinder asBinder() {
frameworks/base/core/java/android/os/storage/IMountService.java:857: method: <android-os-storage-IMountService-Stub-onTransact> : public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

Grep finished (matches found) at Fri Jul 25 14:44:36

其中asInterface的定义:

public static IMountService asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (iin != null && iin instanceof IMountService) {
        return (IMountService) iin;
    }
    return new IMountService.Stub.Proxy(obj);
}

会传进来一个obj,这个obj会被调到queryLocalInterface,最后调到的应该是:

public IInterface queryLocalInterface(String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

这个在Binder.java里。mOwner被设值的地方:

public void attachInterface(IInterface owner, String descriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}

attachInterface 是在MountService的Stub里调用的:

-*- mode: grep; default-directory: "~/src/android/frameworks/base/core/java/android/os/storage/" -*-
Grep started at Sat Jul 26 22:04:08

grep-gtags -e "Stub" -d ~/src/android -o
Found total 2/8 definitions: Entering directory `/home/bhj/src/android'
frameworks/base/core/java/android/os/storage/IMountService.java:37: class: <android.os.storage.IMountService.Stub> : public static abstract class Stub extends Binder implements IMountService {
    37:     public static abstract class Stub extends Binder implements IMountService {
frameworks/base/core/java/android/os/storage/IMountService.java:830: method: <android.os.storage.IMountService.Stub.Stub> : public Stub() {
    830:         public Stub() {
    831:             attachInterface(this, DESCRIPTOR);
    832:         }
    833:

Grep finished (matches found) at Sat Jul 26 22:04:09

再看一下这个Stub的继承树:

-*- mode: compilation; default-directory: "~/src/android/frameworks/base/core/java/android/os/storage/" -*-
Compilation started at Sat Jul 26 22:06:28

ajoke-get-hierarchy.pl android.os.storage.IMountService.Stub -m Stub
make: Entering directory `/home/bhj/src/android'

=> class android.os.storage.IMountService.Stub at frameworks/base/core/java/android/os/storage/IMountService.java line 37.
      public Stub(){
   => class android.os.Binder at frameworks/base/core/java/android/os/Binder.java line 43.
      => interface android.os.IBinder at frameworks/base/core/java/android/os/IBinder.java line 86.
      => class java.lang.Object at libcore/luni/src/main/java/java/lang/Object.java line 131.
   => interface android.os.storage.IMountService at frameworks/base/core/java/android/os/storage/IMountService.java line 35.
         public static abstract class Stub extends Binder implements IMountService{
      => interface android.os.IInterface at frameworks/base/core/java/android/os/IInterface.java line 23.

Compilation finished at Sat Jul 26 22:06:30

这时候就非常清楚的可以看到了,C++里的RTTI(Run Time Type Infomation) 的一种机制,在这个java代码里用obj+descriptor的方式重新实现了一遍。为什 么不直接用isinstanceof呢?有点想不明白。可能是这些源码的作者想尽量保持 java与c++接口名字上的一致性?

回到IMountService,asInterface用到的地方:

asIt-*- mode: grep; default-directory: "~/src/android/frameworks/base/core/java/android/os/storage/" -*-
Grep started at Fri Jul 25 14:53:30

grep-func-call -e "IMountService.*asInterface" --nc -a -v /tests/


Entering directory `/home/bhj/src/android/frameworks/base/core/java/android/app'
ContextImpl.java:2172: <=     private File[] ensureDirsExistOrFilter(File[] dirs) {
ContextImpl.java:2182: =>                         final IMountService mount = IMountService.Stub.asInterface(
Entering directory `/home/bhj/src/android/frameworks/base/core/java/android/os'
Environment.java:74: <=     private static StorageVolume getPrimaryVolume() {
Environment.java:83: =>                         IMountService mountService = IMountService.Stub.asInterface(ServiceManager
Environment.java:707: <=     public static String getStorageState(File path) {
Environment.java:717: =>             final IMountService mountService = IMountService.Stub.asInterface(
Entering directory `/home/bhj/src/android/frameworks/base/core/java/android/os/storage'
IMountService.java:836: <>         public static IMountService asInterface(IBinder obj) {
IMountService.java:857: <=         public boolean onTransact(int code, Parcel data, Parcel reply,
IMountService.java:867: =>                     listener = IMountServiceListener.Stub.asInterface(data.readStrongBinder());
IMountService.java:875: =>                     listener = IMountServiceListener.Stub.asInterface(data.readStrongBinder());
Entering directory `/home/bhj/src/android/frameworks/base/core/java/android/os/storage'
StorageManager.java:315: <=     public StorageManager(ContentResolver resolver, Looper tgtLooper) throws RemoteException {
StorageManager.java:318: =>         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
Entering directory `/home/bhj/src/android/frameworks/base/core/java/com/android/internal/content'
PackageHelper.java:61: <=     public static IMountService getMountService() throws RemoteException {
PackageHelper.java:64: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/frameworks/base/core/java/com/android/internal/os/storage'
ExternalStorageFormatter.java:319: <=     IMountService getMountService() {
ExternalStorageFormatter.java:323: =>                 mMountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/frameworks/base/core/java/com/android/internal/widget'
LockPatternUtils.java:632: <=     private void updateEncryptionPassword(String password) {
LockPatternUtils.java:645: =>         IMountService mountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/frameworks/base/packages/BackupRestoreConfirmation/src/com/android/backupconfirm'
BackupRestoreConfirmation.java:131: <=     public void onCreate(Bundle icicle) {
BackupRestoreConfirmation.java:159: =>         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
Entering directory `/home/bhj/src/android/frameworks/base/packages/SystemUI/src/com/android/systemui/usb'
UsbStorageActivity.java:202: <=     private IMountService getMountService() {
UsbStorageActivity.java:205: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/frameworks/base/services/java/com/android/server'
BackupManagerService.java:773: <=     public BackupManagerService(Context context) {
BackupManagerService.java:781: =>         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
Entering directory `/home/bhj/src/android/frameworks/base/services/java/com/android/server/power'
ShutdownThread.java:295: <=     public void run() {
ShutdownThread.java:374: =>                 final IMountService mount = IMountService.Stub.asInterface(
Entering directory `/home/bhj/src/android/packages/apps/SettingsSmartisan/src/com/android/settings'
CryptKeeperConfirm.java:41: <=         public void onCreate(Bundle savedInstanceState) {
CryptKeeperConfirm.java:72: =>                     IMountService mountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/SettingsSmartisan/src/com/android/settings'
CryptKeeper.java:614: <=     private IMountService getMountService() {
CryptKeeper.java:617: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/SettingsSmartisan/src/com/android/settings/deviceinfo'
Memory.java:214: <=     private synchronized IMountService getMountService() {
Memory.java:218: =>                mMountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/Settings/src/com/android/settings'
CryptKeeperConfirm.java:41: <=         public void onCreate(Bundle savedInstanceState) {
CryptKeeperConfirm.java:72: =>                     IMountService mountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/Settings/src/com/android/settings'
CryptKeeper.java:614: <=     private IMountService getMountService() {
CryptKeeper.java:617: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/Settings/src/com/android/settings/deviceinfo'
Memory.java:203: <=     private synchronized IMountService getMountService() {
Memory.java:207: =>                mMountService = IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/packages/apps/SystemUISmartisan/src/com/smartisanos/systemui/usb'
UsbStorageActivity.java:204: <=     private IMountService getMountService() {
UsbStorageActivity.java:207: =>             return IMountService.Stub.asInterface(service);
Entering directory `/home/bhj/src/android/vendor/qcom/proprietary/QualcommSettings/src/com/qualcomm/qualcommsettings'
QualcommSettings.java:687: <=    private void updateUsbMassStorageStatus() {
QualcommSettings.java:689: =>         IMountService mountService = IMountService.Stub.asInterface(ServiceManager

Grep finished (matches found) at Fri Jul 25 14:53:31

2 ServiceManager.getService()

这个函数:

public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return getIServiceManager().getService(name);
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}

会调到 getIServiceManager:

private static IServiceManager getIServiceManager() {
    if (sServiceManager != null) {
        return sServiceManager;
    }

    // Find the service manager
    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
    return sServiceManager;
}

会调到 ServiceManagerNative.asInterface(BinderInternal.getContextObject())

其中 BinderInternal.getContextObject() 是个native的函数,它会调到 c++ 的 getContextObject:

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
    return getStrongProxyForHandle(0);
}

可以看到,这里的caller参数都没有用到,不知道是为什么要有这个caller参数。

getStrongProxyForHandle的定义如下:

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            b = new BpBinder(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

所以这里我们要看一下 lookupHandleLocked 这个函数,此处传进去的handle为0。

ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    const size_t N=mHandleToObject.size();
    if (N <= (size_t)handle) {
        handle_entry e;
        e.binder = NULL;
        e.refs = NULL;
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return NULL;
    }
    return &mHandleToObject.editItemAt(handle);
}

因为handle为0,所以 mHandleToObject 的.size() 和 handle 一比,如果 handle 不在 mHandleToObject 里,就需要插一个新的 handle_entry 进去,否 则直接从 mHandleToObject 里抽一个 editItemAt 出来交差。

看一下 mHandleToObject 是怎么初始化的:

-*- mode: grep; default-directory: "~/src/android/frameworks/native/libs/binder/" -*-
Grep started at Sun Jul 27 22:16:08

beagrep -e "mHandleToObject"
/home/bhj/src/android/frameworks/native/include/binder/ProcessState.h:96:            Vector<handle_entry>mHandleToObject;
/home/bhj/src/android/frameworks/native/libs/binder/ProcessState.cpp:172:    const size_t N=mHandleToObject.size();
/home/bhj/src/android/frameworks/native/libs/binder/ProcessState.cpp:177:        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
/home/bhj/src/android/frameworks/native/libs/binder/ProcessState.cpp:180:    return &mHandleToObject.editItemAt(handle);

Grep finished (matches found) at Sun Jul 27 22:16:08

完了,mHandleToObject根本没有正规的初始化,都是在 lookupHandleLocked里 完成对它的操作的,如果handle没有找到的话,就放一个handle_entry进去跟这 个handle对应,其binder和refs均为空值。然后在 getStrongProxyForHandle里, 看到了 binder 成员为空,就会 new 一个 BpBinder(handle) 出来赋给它,这 样就建立起了BpBinder与handle之间的关系。

BpBinder有一个mHandle的成员变量,会被赋上这个handle的值,然后这个 mHandle 会在 transact 函数中用到:

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

会调到 IPCThreadState::self()->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) {
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

上面这个函数里所有的多余的注释和 #if 0 之类的代码都已经去掉了。所以其逻辑也比较明了,主要调了一个 writeTransactionData 和一个 waitForResponse 函数。

先看 writeTransactionData

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr; // 这个struct是在kernel里定义的

    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    const status_t err = data.errorCheck(); // status_t 是 system/core/include/utils/Errors.h 里定义的
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = statusBuffer;
        tr.offsets_size = 0;
        tr.data.ptr.offsets = NULL;
    } else {
        return (mLastError = err);
    }

    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));

    return NO_ERROR;
}

code 在 IServiceManager 里是 一个如 Stub.TRANSACTION_registerListener 这样的值。 cmd刚才看到是 BC_TRANSACTION 。在transact调用里, writeTransactionData 的 statusBuffer 的参数是 NULL,所以先不用看。然后 看到的是 mOut 里写了个 cmd,再写了个 tr ,这个函数就返回了,所以它跟内 核里的 binder 设备还没有交互过呢。

交互应该是在 waitForResponse 里:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;

        cmd = mIn.readInt32();

        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;

        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;

        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;

        case BR_ACQUIRE_RESULT:
            {
                ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
                const int32_t result = mIn.readInt32();
                if (!acquireResult) continue;
                *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
            }
            goto finish;

        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->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);
                    } else {
                        err = *static_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            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), this);
                    }
                } else {
                    freeBuffer(NULL,
                        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), this);
                    continue;
                }
            }
            goto finish;

        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }

    return err;
}

最关键的函数应该是那个 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 = (long unsigned int)mOut.data();

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }

    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
    } 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(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) 函数调用。

这个函数最后会调到kernel里:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
        int ret;
        struct binder_proc *proc = filp->private_data;
        struct binder_thread *thread;
        unsigned int size = _IOC_SIZE(cmd);
        void __user *ubuf = (void __user *)arg;

        /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/

        ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
        if (ret)
                return ret;

        mutex_lock(&binder_lock);
        thread = binder_get_thread(proc);
        if (thread == NULL) {
                ret = -ENOMEM;
                goto err;
        }

        switch (cmd) {
        case BINDER_WRITE_READ: {
                struct binder_write_read bwr;
                if (size != sizeof(struct binder_write_read)) {
                        ret = -EINVAL;
                        goto err;
                }
                if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
                        ret = -EFAULT;
                        goto err;
                }
                binder_debug(BINDER_DEBUG_READ_WRITE,
                             "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
                             proc->pid, thread->pid, bwr.write_size, bwr.write_buffer,
                             bwr.read_size, bwr.read_buffer);

                if (bwr.write_size > 0) {
                        ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
                        if (ret < 0) {
                                bwr.read_consumed = 0;
                                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                                        ret = -EFAULT;
                                goto err;
                        }
                }
                if (bwr.read_size > 0) {
                        ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
                        if (!list_empty(&proc->todo))
                                wake_up_interruptible(&proc->wait);
                        if (ret < 0) {
                                if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                                        ret = -EFAULT;
                                goto err;
                        }
                }
                binder_debug(BINDER_DEBUG_READ_WRITE,
                             "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
                             proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
                             bwr.read_consumed, bwr.read_size);
                if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
                        ret = -EFAULT;
                        goto err;
                }
                break;
        }
        case BINDER_SET_MAX_THREADS:
                if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
                        ret = -EINVAL;
                        goto err;
                }
                break;
        case BINDER_SET_CONTEXT_MGR:
                if (binder_context_mgr_node != NULL) {
                        binder_debug(BINDER_DEBUG_TOP_ERRORS,
                                     "binder: BINDER_SET_CONTEXT_MGR already set\n");
                        ret = -EBUSY;
                        goto err;
                }
                ret = security_binder_set_context_mgr(proc->tsk);
                if (ret < 0)
                        goto err;
                if (binder_context_mgr_uid != -1) {
                        if (binder_context_mgr_uid != current->cred->euid) {
                                binder_debug(BINDER_DEBUG_TOP_ERRORS,
                                             "binder: BINDER_SET_"
                                             "CONTEXT_MGR bad uid %d != %d\n",
                                             current->cred->euid,
                                             binder_context_mgr_uid);
                                ret = -EPERM;
                                goto err;
                        }
                } else
                        binder_context_mgr_uid = current->cred->euid;
                binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
                if (binder_context_mgr_node == NULL) {
                        ret = -ENOMEM;
                        goto err;
                }
                binder_context_mgr_node->local_weak_refs++;
                binder_context_mgr_node->local_strong_refs++;
                binder_context_mgr_node->has_strong_ref = 1;
                binder_context_mgr_node->has_weak_ref = 1;
                break;
        case BINDER_THREAD_EXIT:
                binder_debug(BINDER_DEBUG_THREADS, "binder: %d:%d exit\n",
                             proc->pid, thread->pid);
                binder_free_thread(proc, thread);
                thread = NULL;
                break;
        case BINDER_VERSION:
                if (size != sizeof(struct binder_version)) {
                        ret = -EINVAL;
                        goto err;
                }
                if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) {
                        ret = -EINVAL;
                        goto err;
                }
                break;
        default:
                ret = -EINVAL;
                goto err;
        }
        ret = 0;
err:
        if (thread)
                thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
        mutex_unlock(&binder_lock);
        wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
        if (ret && ret != -ERESTARTSYS)
                binder_debug(BINDER_DEBUG_TOP_ERRORS,
                             "binder: %d:%d ioctl %x %lx returned %d\n",
                             proc->pid, current->pid, cmd, arg, ret);
        return ret;
}

上来就 struct binder_proc *proc = filp->private_data; ,这个proc/private_data是在哪里初始化的呢?在binder_open里:

static int binder_open(struct inode *nodp, struct file *filp)
{
        struct binder_proc *proc;

        binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
                     current->group_leader->pid, current->pid);

        proc = kzalloc(sizeof(*proc), GFP_KERNEL);
        if (proc == NULL)
                return -ENOMEM;
        get_task_struct(current);
        proc->tsk = current;
        INIT_LIST_HEAD(&proc->todo);
        init_waitqueue_head(&proc->wait);
        proc->default_priority = task_nice(current);
        mutex_lock(&binder_lock);
        binder_stats_created(BINDER_STAT_PROC);
        hlist_add_head(&proc->proc_node, &binder_procs);
        proc->pid = current->group_leader->pid;
        INIT_LIST_HEAD(&proc->delivered_death);
        filp->private_data = proc;
        mutex_unlock(&binder_lock);

        if (binder_debugfs_dir_entry_proc) {
                char strbuf[11];
                snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
                proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
                        binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
        }

        return 0;
}

binder_open是 binder 设备的打开文件方法:

static const struct file_operations binder_fops = {
        .owner = THIS_MODULE,
        .poll = binder_poll,
        .unlocked_ioctl = binder_ioctl,
        .mmap = binder_mmap,
        .open = binder_open,
        .flush = binder_flush,
        .release = binder_release,
};

如果需要写的话,就会调 binder_thread_write 函数:

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
                        void __user *buffer, int size, signed long *consumed)
{
        uint32_t cmd;
        void __user *ptr = buffer + *consumed;
        void __user *end = buffer + size;

        while (ptr < end && thread->return_error == BR_OK) {
                if (get_user(cmd, (uint32_t __user *)ptr))
                        return -EFAULT;
                ptr += sizeof(uint32_t);
                if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
                        binder_stats.bc[_IOC_NR(cmd)]++;
                        proc->stats.bc[_IOC_NR(cmd)]++;
                        thread->stats.bc[_IOC_NR(cmd)]++;
                }
                switch (cmd) {
                case BC_INCREFS:
                case BC_ACQUIRE:
                case BC_RELEASE:
                case BC_DECREFS: {
                        uint32_t target;
                        struct binder_ref *ref;
                        const char *debug_string;

                        if (get_user(target, (uint32_t __user *)ptr))
                                return -EFAULT;
                        ptr += sizeof(uint32_t);
                        if (target == 0 && binder_context_mgr_node &&
                            (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
                                ref = binder_get_ref_for_node(proc,
                                               binder_context_mgr_node);
                                if (ref->desc != target) {
                                        binder_user_error("binder: %d:"
                                                "%d tried to acquire "
                                                "reference to desc 0, "
                                                "got %d instead\n",
                                                proc->pid, thread->pid,
                                                ref->desc);
                                }
                        } else
                                ref = binder_get_ref(proc, target);
                        if (ref == NULL) {
                                binder_user_error("binder: %d:%d refcou"
                                        "nt change on invalid ref %d\n",
                                        proc->pid, thread->pid, target);
                                break;
                        }
                        switch (cmd) {
                        case BC_INCREFS:
                                debug_string = "IncRefs";
                                binder_inc_ref(ref, 0, NULL);
                                break;
                        case BC_ACQUIRE:
                                debug_string = "Acquire";
                                binder_inc_ref(ref, 1, NULL);
                                break;
                        case BC_RELEASE:
                                debug_string = "Release";
                                binder_dec_ref(ref, 1);
                                break;
                        case BC_DECREFS:
                        default:
                                debug_string = "DecRefs";
                                binder_dec_ref(ref, 0);
                                break;
                        }
                        binder_debug(BINDER_DEBUG_USER_REFS,
                                     "binder: %d:%d %s ref %d desc %d s %d w %d for node %d\n",
                                     proc->pid, thread->pid, debug_string, ref->debug_id,
                                     ref->desc, ref->strong, ref->weak, ref->node->debug_id);
                        break;
                }
                case BC_INCREFS_DONE:
                case BC_ACQUIRE_DONE: {
                        void __user *node_ptr;
                        void *cookie;
                        struct binder_node *node;

                        if (get_user(node_ptr, (void * __user *)ptr))
                                return -EFAULT;
                        ptr += sizeof(void *);
                        if (get_user(cookie, (void * __user *)ptr))
                                return -EFAULT;
                        ptr += sizeof(void *);
                        node = binder_get_node(proc, node_ptr);
                        if (node == NULL) {
                                binder_user_error("binder: %d:%d "
                                        "%s u%p no match\n",
                                        proc->pid, thread->pid,
                                        cmd == BC_INCREFS_DONE ?
                                        "BC_INCREFS_DONE" :
                                        "BC_ACQUIRE_DONE",
                                        node_ptr);
                                break;
                        }
                        if (cookie != node->cookie) {
                                binder_user_error("binder: %d:%d %s u%p node %d"
                                        " cookie mismatch %p != %p\n",
                                        proc->pid, thread->pid,
                                        cmd == BC_INCREFS_DONE ?
                                        "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
                                        node_ptr, node->debug_id,
                                        cookie, node->cookie);
                                break;
                        }
                        if (cmd == BC_ACQUIRE_DONE) {
                                if (node->pending_strong_ref == 0) {
                                        binder_user_error("binder: %d:%d "
                                                "BC_ACQUIRE_DONE node %d has "
                                                "no pending acquire request\n",
                                                proc->pid, thread->pid,
                                                node->debug_id);
                                        break;
                                }
                                node->pending_strong_ref = 0;
                        } else {
                                if (node->pending_weak_ref == 0) {
                                        binder_user_error("binder: %d:%d "
                                                "BC_INCREFS_DONE node %d has "
                                                "no pending increfs request\n",
                                                proc->pid, thread->pid,
                                                node->debug_id);
                                        break;
                                }
                                node->pending_weak_ref = 0;
                        }
                        binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0);
                        binder_debug(BINDER_DEBUG_USER_REFS,
                                     "binder: %d:%d %s node %d ls %d lw %d\n",
                                     proc->pid, thread->pid,
                                     cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
                                     node->debug_id, node->local_strong_refs, node->local_weak_refs);
                        break;
                }
                case BC_ATTEMPT_ACQUIRE:
                        binder_debug(BINDER_DEBUG_TOP_ERRORS,
                                     "binder: BC_ATTEMPT_ACQUIRE not supported\n");
                        return -EINVAL;
                case BC_ACQUIRE_RESULT:
                        binder_debug(BINDER_DEBUG_TOP_ERRORS,
                                     "binder: BC_ACQUIRE_RESULT not supported\n");
                        return -EINVAL;

                case BC_FREE_BUFFER: {
                        void __user *data_ptr;
                        struct binder_buffer *buffer;

                        if (get_user(data_ptr, (void * __user *)ptr))
                                return -EFAULT;
                        ptr += sizeof(void *);

                        buffer = binder_buffer_lookup(proc, data_ptr);
                        if (buffer == NULL) {
                                binder_user_error("binder: %d:%d "
                                        "BC_FREE_BUFFER u%p no match\n",
                                        proc->pid, thread->pid, data_ptr);
                                break;
                        }
                        if (!buffer->allow_user_free) {
                                binder_user_error("binder: %d:%d "
                                        "BC_FREE_BUFFER u%p matched "
                                        "unreturned buffer\n",
                                        proc->pid, thread->pid, data_ptr);
                                break;
                        }
                        binder_debug(BINDER_DEBUG_FREE_BUFFER,
                                     "binder: %d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n",
                                     proc->pid, thread->pid, data_ptr, buffer->debug_id,
                                     buffer->transaction ? "active" : "finished");

                        if (buffer->transaction) {
                                buffer->transaction->buffer = NULL;
                                buffer->transaction = NULL;
                        }
                        if (buffer->async_transaction && buffer->target_node) {
                                BUG_ON(!buffer->target_node->has_async_transaction);
                                if (list_empty(&buffer->target_node->async_todo))
                                        buffer->target_node->has_async_transaction = 0;
                                else
                                        list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
                        }
                        binder_transaction_buffer_release(proc, buffer, NULL);
                        binder_free_buf(proc, buffer);
                        break;
                }

                case BC_TRANSACTION:
                case BC_REPLY: {
                        struct binder_transaction_data tr;

                        if (copy_from_user(&tr, ptr, sizeof(tr)))
                                return -EFAULT;
                        ptr += sizeof(tr);
                        binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
                        break;
                }

                case BC_REGISTER_LOOPER:
                        binder_debug(BINDER_DEBUG_THREADS,
                                     "binder: %d:%d BC_REGISTER_LOOPER\n",
                                     proc->pid, thread->pid);
                        if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {
                                thread->looper |= BINDER_LOOPER_STATE_INVALID;
                                binder_user_error("binder: %d:%d ERROR:"
                                        " BC_REGISTER_LOOPER called "
                                        "after BC_ENTER_LOOPER\n",
                                        proc->pid, thread->pid);
                        } else if (proc->requested_threads == 0) {
                                thread->looper |= BINDER_LOOPER_STATE_INVALID;
                                binder_user_error("binder: %d:%d ERROR:"
                                        " BC_REGISTER_LOOPER called "
                                        "without request\n",
                                        proc->pid, thread->pid);
                        } else {
                                proc->requested_threads--;
                                proc->requested_threads_started++;
                        }
                        thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
                        break;
                case BC_ENTER_LOOPER:
                        binder_debug(BINDER_DEBUG_THREADS,
                                     "binder: %d:%d BC_ENTER_LOOPER\n",
                                     proc->pid, thread->pid);
                        if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
                                thread->looper |= BINDER_LOOPER_STATE_INVALID;
                                binder_user_error("binder: %d:%d ERROR:"
                                        " BC_ENTER_LOOPER called after "
                                        "BC_REGISTER_LOOPER\n",
                                        proc->pid, thread->pid);
                        }
                        thread->looper |= BINDER_LOOPER_STATE_ENTERED;
                        break;
                case BC_EXIT_LOOPER:
                        binder_debug(BINDER_DEBUG_THREADS,
                                     "binder: %d:%d BC_EXIT_LOOPER\n",
                                     proc->pid, thread->pid);
                        thread->looper |= BINDER_LOOPER_STATE_EXITED;
                        break;

                case BC_REQUEST_DEATH_NOTIFICATION:
                case BC_CLEAR_DEATH_NOTIFICATION: {
                        uint32_t target;
                        void __user *cookie;
                        struct binder_ref *ref;
                        struct binder_ref_death *death;

                        if (get_user(target, (uint32_t __user *)ptr))
                                return -EFAULT;
                        ptr += sizeof(uint32_t);
                        if (get_user(cookie, (void __user * __user *)ptr))
                                return -EFAULT;
                        ptr += sizeof(void *);
                        ref = binder_get_ref(proc, target);
                        if (ref == NULL) {
                                binder_user_error("binder: %d:%d %s "
                                        "invalid ref %d\n",
                                        proc->pid, thread->pid,
                                        cmd == BC_REQUEST_DEATH_NOTIFICATION ?
                                        "BC_REQUEST_DEATH_NOTIFICATION" :
                                        "BC_CLEAR_DEATH_NOTIFICATION",
                                        target);
                                break;
                        }

                        binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
                                     "binder: %d:%d %s %p ref %d desc %d s %d w %d for node %d\n",
                                     proc->pid, thread->pid,
                                     cmd == BC_REQUEST_DEATH_NOTIFICATION ?
                                     "BC_REQUEST_DEATH_NOTIFICATION" :
                                     "BC_CLEAR_DEATH_NOTIFICATION",
                                     cookie, ref->debug_id, ref->desc,
                                     ref->strong, ref->weak, ref->node->debug_id);

                        if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
                                if (ref->death) {
                                        binder_user_error("binder: %d:%"
                                                "d BC_REQUEST_DEATH_NOTI"
                                                "FICATION death notific"
                                                "ation already set\n",
                                                proc->pid, thread->pid);
                                        break;
                                }
                                death = kzalloc(sizeof(*death), GFP_KERNEL);
                                if (death == NULL) {
                                        thread->return_error = BR_ERROR;
                                        binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
                                                     "binder: %d:%d "
                                                     "BC_REQUEST_DEATH_NOTIFICATION failed\n",
                                                     proc->pid, thread->pid);
                                        break;
                                }
                                binder_stats_created(BINDER_STAT_DEATH);
                                INIT_LIST_HEAD(&death->work.entry);
                                death->cookie = cookie;
                                ref->death = death;
                                if (ref->node->proc == NULL) {
                                        ref->death->work.type = BINDER_WORK_DEAD_BINDER;
                                        if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
                                                list_add_tail(&ref->death->work.entry, &thread->todo);
                                        } else {
                                                list_add_tail(&ref->death->work.entry, &proc->todo);
                                                wake_up_interruptible(&proc->wait);
                                        }
                                }
                        } else {
                                if (ref->death == NULL) {
                                        binder_user_error("binder: %d:%"
                                                "d BC_CLEAR_DEATH_NOTIFI"
                                                "CATION death notificat"
                                                "ion not active\n",
                                                proc->pid, thread->pid);
                                        break;
                                }
                                death = ref->death;
                                if (death->cookie != cookie) {
                                        binder_user_error("binder: %d:%"
                                                "d BC_CLEAR_DEATH_NOTIFI"
                                                "CATION death notificat"
                                                "ion cookie mismatch "
                                                "%p != %p\n",
                                                proc->pid, thread->pid,
                                                death->cookie, cookie);
                                        break;
                                }
                                ref->death = NULL;
                                if (list_empty(&death->work.entry)) {
                                        death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
                                        if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
                                                list_add_tail(&death->work.entry, &thread->todo);
                                        } else {
                                                list_add_tail(&death->work.entry, &proc->todo);
                                                wake_up_interruptible(&proc->wait);
                                        }
                                } else {
                                        BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);
                                        death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;
                                }
                        }
                } break;
                case BC_DEAD_BINDER_DONE: {
                        struct binder_work *w;
                        void __user *cookie;
                        struct binder_ref_death *death = NULL;
                        if (get_user(cookie, (void __user * __user *)ptr))
                                return -EFAULT;

                        ptr += sizeof(void *);
                        list_for_each_entry(w, &proc->delivered_death, entry) {
                                struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work);
                                if (tmp_death->cookie == cookie) {
                                        death = tmp_death;
                                        break;
                                }
                        }
                        binder_debug(BINDER_DEBUG_DEAD_BINDER,
                                     "binder: %d:%d BC_DEAD_BINDER_DONE %p found %p\n",
                                     proc->pid, thread->pid, cookie, death);
                        if (death == NULL) {
                                binder_user_error("binder: %d:%d BC_DEAD"
                                        "_BINDER_DONE %p not found\n",
                                        proc->pid, thread->pid, cookie);
                                break;
                        }

                        list_del_init(&death->work.entry);
                        if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {
                                death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
                                if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
                                        list_add_tail(&death->work.entry, &thread->todo);
                                } else {
                                        list_add_tail(&death->work.entry, &proc->todo);
                                        wake_up_interruptible(&proc->wait);
                                }
                        }
                } break;

                default:
                        binder_debug(BINDER_DEBUG_TOP_ERRORS,
                                     "binder: %d:%d unknown command %d\n",
                                     proc->pid, thread->pid, cmd);
                        return -EINVAL;
                }
                *consumed = ptr - buffer;
        }
        return 0;
}

2.1 TODO 把binder底下的各个数据结构搞清楚,每个成员变量都是干什么用的。

  • State "TODO" from "" [2014-07-30 Wed 18:26]

2.2 TODO 为什么要 binder_set_nice

  • State "TODO" from "" [2014-07-30 Wed 20:19]

2.3 binder_transaction 函数

static void binder_transaction(struct binder_proc *proc,
                               struct binder_thread *thread,
                               struct binder_transaction_data *tr, int reply)
{
        struct binder_transaction *t;
        struct binder_work *tcomplete;
        size_t *offp, *off_end;
        struct binder_proc *target_proc;
        struct binder_thread *target_thread = NULL;
        struct binder_node *target_node = NULL;
        struct list_head *target_list;
        wait_queue_head_t *target_wait;
        struct binder_transaction *in_reply_to = NULL;
        struct binder_transaction_log_entry *e;
        uint32_t return_error;

        e = binder_transaction_log_add(&binder_transaction_log);
        e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
        e->from_proc = proc->pid;
        e->from_thread = thread->pid;
        e->target_handle = tr->target.handle;
        e->data_size = tr->data_size;
        e->offsets_size = tr->offsets_size;

        if (reply) {
                in_reply_to = thread->transaction_stack;
                if (in_reply_to == NULL) {
                        binder_user_error("binder: %d:%d got reply transaction "
                                          "with no transaction stack\n",
                                          proc->pid, thread->pid);
                        return_error = BR_FAILED_REPLY;
                        goto err_empty_call_stack;
                }
                binder_set_nice(in_reply_to->saved_priority);
                if (in_reply_to->to_thread != thread) {
                        binder_user_error("binder: %d:%d got reply transaction "
                                "with bad transaction stack,"
                                " transaction %d has target %d:%d\n",
                                proc->pid, thread->pid, in_reply_to->debug_id,
                                in_reply_to->to_proc ?
                                in_reply_to->to_proc->pid : 0,
                                in_reply_to->to_thread ?
                                in_reply_to->to_thread->pid : 0);
                        return_error = BR_FAILED_REPLY;
                        in_reply_to = NULL;
                        goto err_bad_call_stack;
                }
                thread->transaction_stack = in_reply_to->to_parent;
                target_thread = in_reply_to->from;
                if (target_thread == NULL) {
                        return_error = BR_DEAD_REPLY;
                        goto err_dead_binder;
                }
                if (target_thread->transaction_stack != in_reply_to) {
                        binder_user_error("binder: %d:%d got reply transaction "
                                "with bad target transaction stack %d, "
                                "expected %d\n",
                                proc->pid, thread->pid,
                                target_thread->transaction_stack ?
                                target_thread->transaction_stack->debug_id : 0,
                                in_reply_to->debug_id);
                        return_error = BR_FAILED_REPLY;
                        in_reply_to = NULL;
                        target_thread = NULL;
                        goto err_dead_binder;
                }
                target_proc = target_thread->proc;
        } else {
                if (tr->target.handle) {
                        struct binder_ref *ref;
                        ref = binder_get_ref(proc, tr->target.handle);
                        if (ref == NULL) {
                                binder_user_error("binder: %d:%d got "
                                        "transaction to invalid handle\n",
                                        proc->pid, thread->pid);
                                return_error = BR_FAILED_REPLY;
                                goto err_invalid_target_handle;
                        }
                        target_node = ref->node;
                } else {
                        target_node = binder_context_mgr_node;
                        if (target_node == NULL) {
                                return_error = BR_DEAD_REPLY;
                                goto err_no_context_mgr_node;
                        }
                }
                e->to_node = target_node->debug_id;
                target_proc = target_node->proc;
                if (target_proc == NULL) {
                        return_error = BR_DEAD_REPLY;
                        goto err_dead_binder;
                }
                if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) {
                        return_error = BR_FAILED_REPLY;
                        goto err_invalid_target_handle;
                }
                if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
                        struct binder_transaction *tmp;
                        tmp = thread->transaction_stack;
                        if (tmp->to_thread != thread) {
                                binder_user_error("binder: %d:%d got new "
                                        "transaction with bad transaction stack"
                                        ", transaction %d has target %d:%d\n",
                                        proc->pid, thread->pid, tmp->debug_id,
                                        tmp->to_proc ? tmp->to_proc->pid : 0,
                                        tmp->to_thread ?
                                        tmp->to_thread->pid : 0);
                                return_error = BR_FAILED_REPLY;
                                goto err_bad_call_stack;
                        }
                        while (tmp) {
                                if (tmp->from && tmp->from->proc == target_proc)
                                        target_thread = tmp->from;
                                tmp = tmp->from_parent;
                        }
                }
        }
        if (target_thread) {
                e->to_thread = target_thread->pid;
                target_list = &target_thread->todo;
                target_wait = &target_thread->wait;
        } else {
                target_list = &target_proc->todo;
                target_wait = &target_proc->wait;
        }
        e->to_proc = target_proc->pid;

        /* TODO: reuse incoming transaction for reply */
        t = kzalloc(sizeof(*t), GFP_KERNEL);
        if (t == NULL) {
                return_error = BR_FAILED_REPLY;
                goto err_alloc_t_failed;
        }
        binder_stats_created(BINDER_STAT_TRANSACTION);

        tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
        if (tcomplete == NULL) {
                return_error = BR_FAILED_REPLY;
                goto err_alloc_tcomplete_failed;
        }
        binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);

        t->debug_id = ++binder_last_id;
        e->debug_id = t->debug_id;

        if (reply)
                binder_debug(BINDER_DEBUG_TRANSACTION,
                             "binder: %d:%d BC_REPLY %d -> %d:%d, "
                             "data %p-%p size %zd-%zd\n",
                             proc->pid, thread->pid, t->debug_id,
                             target_proc->pid, target_thread->pid,
                             tr->data.ptr.buffer, tr->data.ptr.offsets,
                             tr->data_size, tr->offsets_size);
        else
                binder_debug(BINDER_DEBUG_TRANSACTION,
                             "binder: %d:%d BC_TRANSACTION %d -> "
                             "%d - node %d, data %p-%p size %zd-%zd\n",
                             proc->pid, thread->pid, t->debug_id,
                             target_proc->pid, target_node->debug_id,
                             tr->data.ptr.buffer, tr->data.ptr.offsets,
                             tr->data_size, tr->offsets_size);

        if (!reply && !(tr->flags & TF_ONE_WAY))
                t->from = thread;
        else
                t->from = NULL;
        t->sender_euid = proc->tsk->cred->euid;
        t->to_proc = target_proc;
        t->to_thread = target_thread;
        t->code = tr->code;
        t->flags = tr->flags;
        t->priority = task_nice(current);
        t->buffer = binder_alloc_buf(target_proc, tr->data_size,
                tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
        if (t->buffer == NULL) {
                return_error = BR_FAILED_REPLY;
                goto err_binder_alloc_buf_failed;
        }
        t->buffer->allow_user_free = 0;
        t->buffer->debug_id = t->debug_id;
        t->buffer->transaction = t;
        t->buffer->target_node = target_node;
        if (target_node)
                binder_inc_node(target_node, 1, 0, NULL);

        offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));

        if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
                binder_user_error("binder: %d:%d got transaction with invalid "
                        "data ptr\n", proc->pid, thread->pid);
                return_error = BR_FAILED_REPLY;
                goto err_copy_data_failed;
        }
        if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
                binder_user_error("binder: %d:%d got transaction with invalid "
                        "offsets ptr\n", proc->pid, thread->pid);
                return_error = BR_FAILED_REPLY;
                goto err_copy_data_failed;
        }
        if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) {
                binder_user_error("binder: %d:%d got transaction with "
                        "invalid offsets size, %zd\n",
                        proc->pid, thread->pid, tr->offsets_size);
                return_error = BR_FAILED_REPLY;
                goto err_bad_offset;
        }
        off_end = (void *)offp + tr->offsets_size;
        for (; offp < off_end; offp++) {
                struct flat_binder_object *fp;
                if (*offp > t->buffer->data_size - sizeof(*fp) ||
                    t->buffer->data_size < sizeof(*fp) ||
                    !IS_ALIGNED(*offp, sizeof(void *))) {
                        binder_user_error("binder: %d:%d got transaction with "
                                "invalid offset, %zd\n",
                                proc->pid, thread->pid, *offp);
                        return_error = BR_FAILED_REPLY;
                        goto err_bad_offset;
                }
                fp = (struct flat_binder_object *)(t->buffer->data + *offp);
                switch (fp->type) {
                case BINDER_TYPE_BINDER:
                case BINDER_TYPE_WEAK_BINDER: {
                        struct binder_ref *ref;
                        struct binder_node *node = binder_get_node(proc, fp->binder);
                        if (node == NULL) {
                                node = binder_new_node(proc, fp->binder, fp->cookie);
                                if (node == NULL) {
                                        return_error = BR_FAILED_REPLY;
                                        goto err_binder_new_node_failed;
                                }
                                node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
                                node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
                        }
                        if (fp->cookie != node->cookie) {
                                binder_user_error("binder: %d:%d sending u%p "
                                        "node %d, cookie mismatch %p != %p\n",
                                        proc->pid, thread->pid,
                                        fp->binder, node->debug_id,
                                        fp->cookie, node->cookie);
                                goto err_binder_get_ref_for_node_failed;
                        }
                        if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
                                return_error = BR_FAILED_REPLY;
                                goto err_binder_get_ref_for_node_failed;
                        }
                        ref = binder_get_ref_for_node(target_proc, node);
                        if (ref == NULL) {
                                return_error = BR_FAILED_REPLY;
                                goto err_binder_get_ref_for_node_failed;
                        }
                        if (fp->type == BINDER_TYPE_BINDER)
                                fp->type = BINDER_TYPE_HANDLE;
                        else
                                fp->type = BINDER_TYPE_WEAK_HANDLE;
                        fp->handle = ref->desc;
                        binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
                                       &thread->todo);

                        binder_debug(BINDER_DEBUG_TRANSACTION,
                                     "        node %d u%p -> ref %d desc %d\n",
                                     node->debug_id, node->ptr, ref->debug_id,
                                     ref->desc);
                } break;
                case BINDER_TYPE_HANDLE:
                case BINDER_TYPE_WEAK_HANDLE: {
                        struct binder_ref *ref = binder_get_ref(proc, fp->handle);
                        if (ref == NULL) {
                                binder_user_error("binder: %d:%d got "
                                        "transaction with invalid "
                                        "handle, %ld\n", proc->pid,
                                        thread->pid, fp->handle);
                                return_error = BR_FAILED_REPLY;
                                goto err_binder_get_ref_failed;
                        }
                        if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
                                return_error = BR_FAILED_REPLY;
                                goto err_binder_get_ref_failed;
                        }
                        if (ref->node->proc == target_proc) {
                                if (fp->type == BINDER_TYPE_HANDLE)
                                        fp->type = BINDER_TYPE_BINDER;
                                else
                                        fp->type = BINDER_TYPE_WEAK_BINDER;
                                fp->binder = ref->node->ptr;
                                fp->cookie = ref->node->cookie;
                                binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
                                binder_debug(BINDER_DEBUG_TRANSACTION,
                                             "        ref %d desc %d -> node %d u%p\n",
                                             ref->debug_id, ref->desc, ref->node->debug_id,
                                             ref->node->ptr);
                        } else {
                                struct binder_ref *new_ref;
                                new_ref = binder_get_ref_for_node(target_proc, ref->node);
                                if (new_ref == NULL) {
                                        return_error = BR_FAILED_REPLY;
                                        goto err_binder_get_ref_for_node_failed;
                                }
                                fp->handle = new_ref->desc;
                                binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
                                binder_debug(BINDER_DEBUG_TRANSACTION,
                                             "        ref %d desc %d -> ref %d desc %d (node %d)\n",
                                             ref->debug_id, ref->desc, new_ref->debug_id,
                                             new_ref->desc, ref->node->debug_id);
                        }
                } break;

                case BINDER_TYPE_FD: {
                        int target_fd;
                        struct file *file;

                        if (reply) {
                                if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
                                        binder_user_error("binder: %d:%d got reply with fd, %ld, but target does not allow fds\n",
                                                proc->pid, thread->pid, fp->handle);
                                        return_error = BR_FAILED_REPLY;
                                        goto err_fd_not_allowed;
                                }
                        } else if (!target_node->accept_fds) {
                                binder_user_error("binder: %d:%d got transaction with fd, %ld, but target does not allow fds\n",
                                        proc->pid, thread->pid, fp->handle);
                                return_error = BR_FAILED_REPLY;
                                goto err_fd_not_allowed;
                        }

                        file = fget(fp->handle);
                        if (file == NULL) {
                                binder_user_error("binder: %d:%d got transaction with invalid fd, %ld\n",
                                        proc->pid, thread->pid, fp->handle);
                                return_error = BR_FAILED_REPLY;
                                goto err_fget_failed;
                        }
                        if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file) < 0) {
                                fput(file);
                                return_error = BR_FAILED_REPLY;
                                goto err_get_unused_fd_failed;
                        }
                        target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
                        if (target_fd < 0) {
                                fput(file);
                                return_error = BR_FAILED_REPLY;
                                goto err_get_unused_fd_failed;
                        }
                        task_fd_install(target_proc, target_fd, file);
                        binder_debug(BINDER_DEBUG_TRANSACTION,
                                     "        fd %ld -> %d\n", fp->handle, target_fd);
                        /* TODO: fput? */
                        fp->handle = target_fd;
                } break;

                default:
                        binder_user_error("binder: %d:%d got transactio"
                                "n with invalid object type, %lx\n",
                                proc->pid, thread->pid, fp->type);
                        return_error = BR_FAILED_REPLY;
                        goto err_bad_object_type;
                }
        }
        if (reply) {
                BUG_ON(t->buffer->async_transaction != 0);
                binder_pop_transaction(target_thread, in_reply_to);
        } else if (!(t->flags & TF_ONE_WAY)) {
                BUG_ON(t->buffer->async_transaction != 0);
                t->need_reply = 1;
                t->from_parent = thread->transaction_stack;
                thread->transaction_stack = t;
        } else {
                BUG_ON(target_node == NULL);
                BUG_ON(t->buffer->async_transaction != 1);
                if (target_node->has_async_transaction) {
                        target_list = &target_node->async_todo;
                        target_wait = NULL;
                } else
                        target_node->has_async_transaction = 1;
        }
        t->work.type = BINDER_WORK_TRANSACTION;
        list_add_tail(&t->work.entry, target_list);
        tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
        list_add_tail(&tcomplete->entry, &thread->todo);
        if (target_wait)
                wake_up_interruptible(target_wait);
        return;

err_get_unused_fd_failed:
err_fget_failed:
err_fd_not_allowed:
err_binder_get_ref_for_node_failed:
err_binder_get_ref_failed:
err_binder_new_node_failed:
err_bad_object_type:
err_bad_offset:
err_copy_data_failed:
        binder_transaction_buffer_release(target_proc, t->buffer, offp);
        t->buffer->transaction = NULL;
        binder_free_buf(target_proc, t->buffer);
err_binder_alloc_buf_failed:
        kfree(tcomplete);
        binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
err_alloc_tcomplete_failed:
        kfree(t);
        binder_stats_deleted(BINDER_STAT_TRANSACTION);
err_alloc_t_failed:
err_bad_call_stack:
err_empty_call_stack:
err_dead_binder:
err_invalid_target_handle:
err_no_context_mgr_node:
        binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
                     "binder: %d:%d transaction failed %d, size %zd-%zd\n",
                     proc->pid, thread->pid, return_error,
                     tr->data_size, tr->offsets_size);

        {
                struct binder_transaction_log_entry *fe;
                fe = binder_transaction_log_add(&binder_transaction_log_failed);
                *fe = *e;
        }

        BUG_ON(thread->return_error != BR_OK);
        if (in_reply_to) {
                thread->return_error = BR_TRANSACTION_COMPLETE;
                binder_send_failed_reply(in_reply_to, return_error);
        } else
                thread->return_error = return_error;
}

这个函数会先把数据从user space拷到kernel space里来。然后,对于数据中的 “对象”,也就是那些Binder/Handle/FileDescriptor进行处理,具体的做法就是, 如果看到一个Binder,就把它改成相应的Handle;看到一个Handle,就把它改成 相应的Binder(Binder Node)。而如果看到一个fd文件描述符的话,就把它从 呼出进程拷贝到呼入进程。通过BinderNode(一个结构体、类、对象?)与 Handle之间相互切换,在呼出进程(调用方、发起方)和呼入进程(被调用方、 接受方)之间建立起了相互的对应关系。这个关系就像tcp/ip里一个ip地址+一 个tpc端口对应着另一端的一个ip地址+一个tcp端口,非常地高大上,是吧!

中间会做一系列的错误检测,比如Handle和BinderNode都必须保证是能找得到的, 如果发现找不到的话,就表明出错了。又比如看到fd的话,要检查被调用的 BinderNode是不是接受fd的,有一些Binder的接口根本不涉及到fd,你给它传一 个fd进去,也肯定是出错了。

最后,我们看到的那个ioctl还会调用到binder_thread_read,这个函数是会 block的,binder_thread_write函数不会,它把需要工作的内容贴出去就好了。 但如果它需要往回读的话,那一般就要贴出去的工作被处理完了才能返回来数据 以供读取。

或者是那种server,那么他一般不会发起bc_transaction,主动给别人写数据, 而是一上来就进入读等待,当有client把工作内容贴过来之后,server可以给一 个br_reply,这样client就只需要读回去,但也有可能server可以贴一个 br_transaction,返回去的数据需要继续进行br_transaction。不管是 bc_transaciton还是br_transaction,它们都会对应一个交易码,通过这个交易 码,最后又会对应到一段可执行的代码,从而达到进程间的代码调用。

binder_thread_read函数返回之后,一般会回到 waitForResponse 这个函数里, 它是最普通的进行binder IPC调用的函数。我们看看它如果读到Binder应答会做些什么吧。

首先,如果读回来的应答是一个BR_REPLY的话,会读用 reply->ipcSetDataReference(),这样 reply 这个Parcel就会指向内核传上 来的读据,这块数据所在的buffer是在内核和用户空间里都有映射的,在用户空 间里是一个只读的映射。这样,上层的代码可以读取这个数据,没有问题。

然后除了一些特殊情况比如 BR_DEAD_REPLY 之类异常的情况之外,最后代码 default调用了 err = executeCommand(cmd); ,在这个函数里, BR_TRANSACTION得到了处理:

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) {
                // We have inherited a reduced priority from the caller, but do not
                // want to run in that state in this process.  The driver set our
                // priority already (though not our scheduling class), so bounce
                // it back to the default before invoking the transaction.
                setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
            }
        } else {
            if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
                // We want to use the inherited priority from the caller.
                // Ensure this thread is in the background scheduling class,
                // since the driver won't modify scheduling classes for us.
                // The scheduling group is reset to default by the caller
                // once this method returns after the transaction is complete.
                set_sched_policy(mMyThreadId, SP_BACKGROUND);
            }
        }

        //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);

        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 {
            const status_t error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            if (error < NO_ERROR) reply.setError(error);
        }

        //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
        //     mCallingPid, origPid, origUid);

        if ((tr.flags & TF_ONE_WAY) == 0) {
            LOG_ONEWAY("Sending reply to %d!", mCallingPid);
            sendReply(reply, 0);
        } else {
            LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
        }

        mCallingPid = origPid;
        mCallingUid = origUid;

        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
                << tr.target.ptr << ": " << indent << reply << dedent << endl;
        }

    }
    break;

注意这段代码:

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);

}

在这里直接把tr.cookie cast成一个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;
}

如果是 PING_TRANSACTION 交易码的话,直接处理了,否则就调用 onTransact 函数,注意这是一个虚函数!所以最后被调用的应该是各个 Binder 服务自己的 交易逻辑,这样我们就回到了 IMountService 的服务处理:

哦,请稍等一下, IMountService 它是一个Java的代码,我们需要看一下Java的Binder实现。

基本上呢,在JNI里会有一个JavaBBinder,奥妙全在这里面。首先,它实现了一 个onTransact虚函数,在里面它会调用java的Binder对象的 execTransact 私有 函数,这个Java函数的调用接口在Jni初始化的时候保存在了 mExecTransact 里。 最后,这个 execTransact 函数会调用 Java 的 onTransact 函数。

好了,代码读到这里,差不多我自己对Binder的实现有了一个比以前更清晰的了 解,您呢?