Binder 通讯数据流概览

The information below comes from a number of sources, including my own experiments with the Android IPC and some disparate internet sources.

The overall architecture of the Android IPC system is shown in the diagram below. It consists of four major blocks; one in kernel space, and the other three in user space. The dashed lines represent the logical RPC calls. The solid lines represent the actual
data flow.

  • BinderDriver: This is the core of IPC system. It passes data between a ServiceProvider(s) and a ServiceUser(s). This kernel component is provided by Android.
  • ServiceProvider: Provides some kind of service. It parses the received RPC data from the BinderDriver and does the real work. Application developers will either make use of existing
    service providers (such as the Camera or AudioFlinger), or in some cases will write their own.
  • ServiceManager: This is a special singleton ServiceProvider that provides service manager services for other service providers. This component is provided by Android.
  • ServiceUser: This is the client. It remote calls the ServiceProvider by generating an RPC and sending it to the BinderDriver. Application developers typically write their own ServiceUser
    as part of their application.

Here is a typical flow of events for a fictitious MultServiceProvider (a service provider that multiplies two numbers for a client) and a MultServiceUser client which doesn’t know how to do multiplication (maybe because the numbers are quaternions) and needs
to use the MultServiceProvider:

  1. ServiceManager runs first (at power-up) and registers a special node (node O) with the BinderDriver.
  2. The MultServiceProvider gets an IServiceManager proxy object for the special node O by calling the global “defaultServiceManager()” function.
  3. The MultServiceProvider then calls defaultServiceManager()->addService(“Multiplier”, new MultServiceProvider()) to add itself as a service provider and then waits in an infinite loop
    for someone to request its services. The addService RPC call is routed to the ServiceManager through the BinderDriver.
  4. The BinderDriver notices that the RPC is for the ServiceManager to add a new service, so besides routing the RPC to the ServiceManager it generates another node (let’s call it node
    M), for the new MultServiceProvider.
  5. The ServiceManager reads the data from the BinderDriver and processes the IServiceManager::addService RPC call.
  6. The MultServiceUser client process gets an IServiceManager proxy object for the special node O (again by using defaultServiceManager()).
  7. The client does an IServiceManager::getService(“Multiplier”) RPC call to get the MultServiceProvider. This call is routed to the ServiceManager through the BinderDriver.
  8. The ServiceManager reads the RPC data from the BinderDriver, processes the IServiceManager::getService request and returns back the node representing the MultServiceProvider.
  9. MultServiceUser calls MultServiceProvider::multiply(a, b). This call is routed to  the MultServiceProvider by the BinderDriver.
  10. The MultServiceProvider handles the MultServiceProvider::multiply RPC call and sends the product of the 2 numbers in a reply to the BinderDriver.
  11. The BinderDriver routes the reply back to the client.
  12. The client reads the data from the BinderDriver which contains the result of “a * b”.

In a future post I hope to discuss the whole architecture in more detail, with concrete code examples for how to use IBinder, IInterface, BBinder, BpInterface, BnInterface, etc… to create a ServiceProvider and a ServiceUser all in native C++ code on Android.

This is a follow-up (with actual code examples) to a post I wrote a while ago on how to use theAndroid
IPC system
 from native C++ code. The code is hosted on GitHub at Android IPC binder demo. A number of readers have
asked for sample code after reading the previous post, so hopefully this helps.

When executed without any arguments, the binary acts as a service (named “Demo”). When executed with an integer argument (ex: “binder 743″), the binary acts as a client that searches for the “Demo” service, binds to it, and exercises its API. To keep things
simple, there’s virtually no error checking. Some debug messages are sent to stdout, and others to logcat.

The suggested way to run this demo to have 3 windows open and issue the following commands in them:

  1. adb logcat -v time binder_demo:* *:S
  2. adb shell binder
  3. adb shell binder 456

Now for a brief explanation of the code. The IInterface, BpInterface, and BnInterface classes are provided by the Android framework.

We start by defining an interface (think AIDL) that will be shared between the service and the client:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

class
IDemo : public
IInterface {

    public:

        enum
{

            ALERT = IBinder::FIRST_CALL_TRANSACTION,

            PUSH,

            ADD

        };

        // Sends a user-provided value to the service

        virtual
void       
push(int32_t data)          = 0;

        // Sends a fixed alert string to the service

        virtual
void       
alert()                     = 0;

        // Requests the service to perform an addition and return the result

        virtual
int32_t     add(int32_t v1, int32_t v2) = 0;

        DECLARE_META_INTERFACE(Demo);

};

// This implementation macro would normally go in a cpp file

IMPLEMENT_META_INTERFACE(Demo,
"Demo");

Next we define the server end, which is made up of 2 classes: BnDemo, and its derived class, Demo. BnDemo extracts the arguments from the data Parcel sent by the client, calls the appropriate virtual function (implemented in the Demo class) to do the heavy-lifting,
and packs the returned values (if any) into a reply Parcel to be sent back to the client.

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

class
BnDemo : public
BnInterface<IDemo> {

    virtual
status_t onTransact(uint32_t code, const
Parcel& data,

                                Parcel* reply, uint32_t flags = 0);

};

status_t BnDemo::onTransact(uint32_t code,
const
Parcel& data,

                            Parcel* reply, uint32_t flags) {

    data.checkInterface(this);

    switch(code) {

        case
ALERT: {

            alert();

            return
NO_ERROR;

        }
break;

        case
PUSH: {

            int32_t inData = data.readInt32();

            push(inData);

            return
NO_ERROR;

        }
break;

        case
ADD: {

            int32_t inV1 = data.readInt32();

            int32_t inV2 = data.readInt32();

            int32_t sum = add(inV1, inV2);

            reply->writeInt32(sum);

            return
NO_ERROR;

        }
break;

        default:

            return
BBinder::onTransact(code, data, reply, flags);

    }

}

This is the Demo class, which would normally do the real work on the service side of the binder.

?


1

2

3

4

5

6

7

8

9

10

11

class
Demo : public
BnDemo {

    virtual
void
push(int32_t data) {

        // Do something with the data the client pushed

    }

    virtual
void
alert() {

        // Handle the alert

    }

    virtual
int32_t add(int32_t v1, int32_t v2) {

        return
v1 + v2;

    }

};

Now we define a service proxy, to be used on the client side. Notice again that any data the client needs to send to the service is packed in a Parcel and results (if any) are also returned in a Parcel.

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

class
BpDemo : public
BpInterface<IDemo> {

    public:

        BpDemo(const
sp<IBinder>& impl) : BpInterface<IDemo>(impl) { }

        virtual
void
push(int32_t push_data) {

            Parcel data, reply;

            data.writeInterfaceToken(IDemo::getInterfaceDescriptor());

            data.writeInt32(push_data);

            remote()->transact(PUSH, data, &reply);

        }

        virtual
void
alert() {

            Parcel data, reply;

            data.writeInterfaceToken(IDemo::getInterfaceDescriptor());

            remote()->transact(ALERT, data, &reply, IBinder::FLAG_ONEWAY);

        }

        virtual
int32_t add(int32_t v1, int32_t v2) {

            Parcel data, reply;

            data.writeInterfaceToken(IDemo::getInterfaceDescriptor());

            data.writeInt32(v1);

            data.writeInt32(v2);

            remote()->transact(ADD, data, &reply);

            int32_t res;

            status_t status = reply.readInt32(&res);

            return
res;

        }

};

Finally, we start the service as follows:

?


1

2

defaultServiceManager()->addService(String16("Demo"),
new
Demo());

android::ProcessState::self()->startThreadPool();

And the client can now connect to the service and call some of the provided functions:

?


1

2

3

4

5

6

7

sp<IServiceManager> sm = defaultServiceManager();

sp<IBinder> binder = sm->getService(String16("Demo"));

sp<IDemo> demo = interface_cast<IDemo>(binder);

demo->alert();

demo->push(65);

int32_t sum = demo->add(453, 827);

There’s a lot more that could be said, but I’m not planning on writing the book on the subject. If you’ve made it this far, you should be able to figure out the rest. The full compilable code is atBinderDemo.

/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
  vi:ai:tabstop=8:shiftwidth=4:softtabstop=4:expandtab
  */
   
  /*
  * Author: Gabriel Burca <gburca dash binder at ebixio dot com>
  *
  * Sample code for using binders in Android from C++
  *
  * The Demo service provides 3 operations: push(), alert(), add(). See
  * the IDemo class documentation to see what they do.
  *
  * Both the server and client code are included below.
  *
  * To view the log output:
  * adb logcat -v time binder_demo:* *:S
  *
  * To run, create 2 adb shell sessions. In the first one run "binder" with no
  * arguments to start the service. In the second one run "binder N" where N is
  * an integer, to start a client that connects to the service and calls push(N),
  * alert(), and add(N, 5).
  */
   
  #define
LOG_TAG "binder_demo"
   
  /* For relevant code see:
  frameworks/base/{include,libs}/binder/{IInterface,Parcel}.h
  frameworks/base/include/utils/{Errors,RefBase}.h
  */
   
  #include
<stdlib.h>
   
  #include
"utils/RefBase.h"
  #include
"utils/Log.h"
  #include
"utils/TextOutput.h"
   
  #include
<binder/IInterface.h>
  #include
<binder/IBinder.h>
  #include
<binder/ProcessState.h>
  #include
<binder/IServiceManager.h>
  #include
<binder/IPCThreadState.h>
   
  using
namespace android;
   
   
  #define
INFO(...) \
  do { \
  printf(__VA_ARGS__); \
  printf("\n"); \
  LOGD(__VA_ARGS__); \
  } while(0)
   
  void
assert_fail(const
char *file, int line,
const
char *func, const
char *expr) {
  INFO("assertion failed at file
%s, line
%d, function %s:",
  file, line, func);
  INFO("%s", expr);
  abort();
  }
   
  #define
ASSERT(e) \
  do { \
  if (!(e)) \
  assert_fail(__FILE__, __LINE__, __func__, #e); \
  } while(0)
   
   
  // Where to print the parcel contents: aout, alog, aerr. alog doesn‘t seem to work.
  #define
PLOG aout
   
   
   
  // Interface (our AIDL) - Shared by server and client
  class
IDemo : public
IInterface {
  public:
  enum {
  ALERT = IBinder::FIRST_CALL_TRANSACTION,
  PUSH,
  ADD
  };
  // Sends a user-provided value to the service
  virtual
void push(int32_t data) = 0;
  // Sends a fixed alert string to the service
  virtual
void alert() = 0;
  // Requests the service to perform an addition and return the result
  virtual
int32_t add(int32_t v1,
int32_t v2) = 0;
   
  DECLARE_META_INTERFACE(Demo);
// Expands to 5 lines below:
  //static const android::String16 descriptor;
  //static android::sp<IDemo> asInterface(const android::sp<android::IBinder>& obj);
  //virtual const android::String16& getInterfaceDescriptor() const;
  //IDemo();
  //virtual ~IDemo();
  };
   
  // Client
  class
BpDemo : public
BpInterface<IDemo> {
  public:
  BpDemo(const sp<IBinder>& impl) : BpInterface<IDemo>(impl) {
  LOGD("BpDemo::BpDemo()");
  }
   
  virtual
void push(int32_t push_data) {
  Parcel data, reply;
  data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
  data.writeInt32(push_data);
   
  aout << "BpDemo::push parcel to be sent:\n";
  data.print(PLOG);
endl(PLOG);
   
  remote()->transact(PUSH, data, &reply);
   
  aout << "BpDemo::push parcel reply:\n";
  reply.print(PLOG);
endl(PLOG);
   
  LOGD("BpDemo::push(%i)",
push_data);
  }
   
  virtual
void alert() {
  Parcel data, reply;
  data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
  data.writeString16(String16("The alert string"));
  remote()->transact(ALERT, data, &reply, IBinder::FLAG_ONEWAY);
// asynchronous call
  LOGD("BpDemo::alert()");
  }
   
  virtual
int32_t add(int32_t v1,
int32_t v2) {
  Parcel data, reply;
  data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
  data.writeInt32(v1);
  data.writeInt32(v2);
  aout << "BpDemo::add parcel to be sent:\n";
  data.print(PLOG);
endl(PLOG);
  remote()->transact(ADD, data, &reply);
  LOGD("BpDemo::add transact reply");
  reply.print(PLOG);
endl(PLOG);
   
  int32_t res;
  status_t status = reply.readInt32(&res);
  LOGD("BpDemo::add(%i,
%i) =
%i (status: %i)", v1, v2, res, status);
  return res;
  }
  };
   
  //IMPLEMENT_META_INTERFACE(Demo, "Demo");
  // Macro above expands to code below. Doing it by hand so we can log ctor and destructor calls.
  const android::String16
IDemo::descriptor("Demo");
  const android::String16&
IDemo::getInterfaceDescriptor()
const {
  return IDemo::descriptor;
  }
  android::sp<IDemo> IDemo::asInterface(const android::sp<android::IBinder>& obj) {
  android::sp<IDemo> intr;
  if (obj !=
NULL) {
  intr = static_cast<IDemo*>(obj->queryLocalInterface(IDemo::descriptor).get());
  if (intr ==
NULL) {
  intr = new
BpDemo(obj);
  }
  }
  return intr;
  }
  IDemo::IDemo() {
LOGD("IDemo::IDemo()"); }
  IDemo::~IDemo() {
LOGD("IDemo::~IDemo()"); }
  // End of macro expansion
   
  // Server
  class
BnDemo : public
BnInterface<IDemo> {
  virtual
status_t onTransact(uint32_t code,
const Parcel& data, Parcel* reply,
uint32_t flags =
0);
  };
   
  status_t
BnDemo::onTransact(uint32_t code,
const Parcel& data, Parcel* reply,
uint32_t flags) {
  LOGD("BnDemo::onTransact(%i)
%i", code, flags);
  data.checkInterface(this);
  data.print(PLOG);
endl(PLOG);
   
  switch(code) {
  case ALERT: {
  alert();
// Ignoring the fixed alert string
  return NO_ERROR;
  } break;
  case PUSH: {
  int32_t inData = data.readInt32();
  LOGD("BnDemo::onTransact got
%i", inData);
  push(inData);
  ASSERT(reply !=
0);
  reply->print(PLOG);
endl(PLOG);
  return NO_ERROR;
  } break;
  case ADD: {
  int32_t inV1 = data.readInt32();
  int32_t inV2 = data.readInt32();
  int32_t sum =
add(inV1, inV2);
  LOGD("BnDemo::onTransact add(%i,
%i) =
%i", inV1, inV2, sum);
  ASSERT(reply !=
0);
  reply->print(PLOG);
endl(PLOG);
  reply->writeInt32(sum);
  return NO_ERROR;
  } break;
  default:
  return
BBinder::onTransact(code, data, reply, flags);
  }
  }
   
  class
Demo : public
BnDemo {
  virtual
void push(int32_t data) {
  INFO("Demo::push(%i)",
data);
  }
  virtual
void alert() {
  INFO("Demo::alert()");
  }
  virtual
int32_t add(int32_t v1,
int32_t v2) {
  INFO("Demo::add(%i,
%i)", v1, v2);
  return v1 + v2;
  }
  };
   
   
  // Helper function to get a hold of the "Demo" service.
  sp<IDemo> getDemoServ() {
  sp<IServiceManager> sm = defaultServiceManager();
  ASSERT(sm !=
0);
  sp<IBinder> binder = sm->getService(String16("Demo"));
  // TODO: If the "Demo" service is not running, getService times out and binder == 0.
  ASSERT(binder !=
0);
  sp<IDemo> demo = interface_cast<IDemo>(binder);
  ASSERT(demo !=
0);
  return demo;
  }
   
   
  int
main(int argc,
char **argv) {
   
  if (argc ==
1) {
  LOGD("We‘re the service");
   
  defaultServiceManager()->addService(String16("Demo"),
new
Demo());
  android::ProcessState::self()->startThreadPool();
  LOGD("Demo service is now ready");
  IPCThreadState::self()->joinThreadPool();
  LOGD("Demo service thread joined");
  } else
if (argc == 2) {
  INFO("We‘re the client:
%s", argv[1]);
   
  int v =
atoi(argv[1]);
   
  sp<IDemo> demo = getDemoServ();
  demo->alert();
  demo->push(v);
  const
int32_t adder = 5;
  int32_t sum = demo->add(v, adder);
  LOGD("Addition result:
%i +
%i = %i", v, adder, sum);
  }
   
  return
0;
  }
时间: 2024-08-04 20:09:56

Binder 通讯数据流概览的相关文章

Android下通过root实现对system_server中binder的ioctl调用拦截

Android下通过root实现对system_server中binder的ioctl调用拦截 分类: Android2013-06-19 18:09 779人阅读 评论(0) 收藏 举报 作 者: Passion时 间: 2012-10-18,13:53:53链 接: http://bbs.pediy.com/showthread.php?t=157419 Android下通过root实现对system_server中binder的ioctl调用拦截作者:passion2012-10-18关键

LocalBroadcastManager 的实现原理,Handler还是 Binder?

原文: http://www.trinea.cn/android/localbroadcastmanager-impl/ 对 LocalBroadcastManager 大家应该都不陌生,相对 BroadcastReceiver,它只能用于应用内通信,安全性更好,同时拥有更高的运行效率.也是需要发送应用内广播时的官方推荐. 大家也都知道BroadcastReceiver的通信是走 Binder 机制的,而 LocalBroadcastManager 因为叫LocalBroadcast,可能让人产

Android中的Binder机制的简要理解

转载自:http://www.linuxidc.com/Linux/2012-07/66195.htm http://blog.csdn.net/sunxingzhesunjinbiao/article/details/42195013 我们知道,在Android系统中,每一个应用程序都运行在独立的进程中,这也保证了当其中一个程序出现异常而不会影响另一个应用程序的正常运转.在许多情况下,我们activity都会与各种系统的service打交道,很显然,我们写的程序中activity与系统serv

Binder机制1---Binder原理介绍

1.Binder通信机制介绍 这篇文章会先对照Binder机制与Linux的通信机制的区别,了解为什么Android会另起炉灶,採用Binder.接着,会依据Binder的机制,去理解什么是Service Manager,在C/S模型中扮演什么角色.最后,会从一次完整的通信活动中,去理解Binder通信的过程. 1.1 Android与Linux通信机制的比較 尽管Android继承使用Linux的内核,但Linux与Android的通信机制不同. 在Linux中使用的IPC通信机制例如以下:

Android binder学习一:主要概念

要看得懂android代码,首先要了解binder机制.binder机制也是android里面比較难以理解的一块,这里记录一下binder的重要概念以及实现.作为备忘. 部分内容来源于网上,如有侵权.请及时告知. 1.binder通信机制概述 binder通信是一种client-server的通信结构, 1.从表面上来看.是client通过获得一个server的代理接口,对server进行直接调用: 2.实际上,代理接口中定义的方法与server中定义的方法是一一相应的. 3.client调用某

android Binder机制

Binder 架构设计 Binder 被设计出来是解决 Android IPC(进程间通信) 问题的.Binder 将两个进程间交互的理解为 Client 向 Server 进行通信. 如下:binder总体架构图 如上图所示,Binder 架构分为 Client.Server.Service Manager 和 Binder Driver. Client: 服务调用者,一般就是我们应用开发者,通过调用诸如List<PackageInfo> packs = getActivity().getP

[Android] 彻底了解Binder机制原理和底层实现

1.Binder通信机制介绍 这篇文章会先对比Binder机制与Linux的通信机制的差别,了解为什么Android会另起炉灶,采用Binder.接着,会根据 Binder的机制,去理解什么是Service  Manager,在C/S模型中扮演什么角色.最后,会从一次完整的通信活动中,去理解Binder通信的过程. 1.1 Android与Linux通信机制的比较 虽然Android继承使用Linux的内核,但Linux与Android的通信机制不同. 在Linux中使用的IPC通信机制如下:

[深入理解Android卷一全文-第六章]深入理解Binder

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第6章 深入理解Binder 本章主要内容 ·  以MediaServer为切入点,对Binder的工作机制进行分析. ·  剖析ServiceManager的原理. ·  以MediaPlayerService为切入点对Client和Service的交互进行分析. ·  学以致用,探讨如何写自己的Servi

Android系统中基于Binder的IPC流程框架分析

前言: Activity.Service.BroadcastReceiver.Content Provider是Android的四大应用程序组件,构成一个完整的应用程序的这些组件可以在同一个进程,也可以不在同一个进程,而当这些组件不在同一个进程,需要进行数据交互时就需要一种IPC(Inter-Process Communication)进程间通信机制来完成,而Binder就是提供了IPC功能的一个框架.实现IPC的整个Binder框架包含几个重要组成部分,它们分别是Binder Driver.C