Android’s Binder mechanism is simple
Android’s Binder mechanism is simple
A description
Binder is the most common Android operating system, and the most difficult for beginners to understand. Binder is the mechanism through which many services communicate with clients. So understanding Binder is a great way to understand the flow of your programs.
Binder is an example of MediaService:
L ServiceManager, which is the manager of the entire Android OS service
L MediaService, this program registers MediaPlayerService to provide media playback service, we will only analyze this
L MediaPlayerClient, this is the client program that interacts with MediaPlayerService
Let’s start with the MediaService application.
The birth of MediaService
MediaService is an application, and while Android makes all the JAVA stuff, it’s still a full Linux operating system at heart, and no applications are written in JAVA yet. So MS(MediaService) is the same thing as a normal C++ application.
The source file for MediaService is in: Framework \ Base \Media\ Mediaservermain_mediaserver.cpp. Let’s see what it is!
int main(int argc,char** argv)
{
// this is not a problem.
// Get an instance of ProcessState
sp<ProcessState>proc(ProcessState::self());
// Get a ServiceManager object
sp<IServiceManager> sm =defaultServiceManager();
MediaPlayerService::instantiate(); // Initialize MediaPlayerService
ProcessState::self()->startThreadPool(); // Start the thread pool of Process.
IPCThreadState::self()->joinThreadPool(); // Add yourself to the thread pool.
}
Of these, we only analyze MediaPlayerService.
So many questions, it seems that we only have one function after another in-depth analysis. However, here is a brief introduction to sp.
Sp; smartPointer; strong pointer In fact, I later found that do not pay too much attention to this, put it as a common pointer, that is, SP <IServiceManager>====== IServiceManager*. Sp is Google came out in order to facilitate C/C++ programmers to manage pointer allocation and release of a set of methods, similar to what WeakReference JAVA. Personally, I think if you were writing your own program, you could do without it.
In future analysis, sp<XXX> can be regarded as XXX*.
2.1 ProcessState
The first function called is ProcessState::self(), which assigns a value to the proc variable. When the program runs, the Proc automatically deletes the internal contents, freeing the previously allocated resources.
The ProcessState location is framework\ Base \libs\binder\ processState.cpp
sp<ProcessState>ProcessState::self()
{
if (gProcess ! = NULL) return gProcess; —-> Definitely not here the first time you come in
AutoMutex _l(gProcessMutex); — – > lock protection
if (gProcess == NULL) gProcess = newProcessState; Create a ProcessState object
returngProcess; See, this returns a pointer, but the function returns sp< XXX >, so
// use sp< XXX > as XXX*
}
Look again at the ProcessState constructor
// This constructor looks important
ProcessState::ProcessState()
: mDriverFD(open_driver())—–>Android: mDriverFD(open_driver())—–>Android: mDriverFD(open_driver())—–>Android: mDriverFD(open_driver())—–>Android: mDriverFD(open_driver())—–
, mVMStart(MAP_FAILED)// Start address of mapped memory
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if(mDriverFD >= 0) {
//BIDNER_VM_SIZE is defined as (1*1024*1024) – (4096 *2) 1m-8K
mVMStart = mmap(0, BINDER_VM_SIZE,PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
mDriverFD, 0); // This is a man mmap
// Map fd to memory, so that memory memcpy operations are equivalent to write/read(fd)
}
.
}
I hate adding a function to the constructor list because I often forget that the initialization of a variable is the result of a function call.
Open_driver open_driver open_driver open_driver open_driver open_driver open_driver open_driver /dev/binder
A virtual device set up for interprocess communication. BTW is a mechanism provided by the kernel, and we use socket and NET_LINK to communicate with the kernel is the same reason.
static intopen_driver()
{
int fd = open(“/dev/binder”,O_RDWR); / / open/dev/binder
if (fd >= 0) {
.
size_t maxThreads = 15;
// Ioctl tells the kernel that the maximum number of threads supported by this fd is 15.
result = ioctl(fd,BINDER_SET_MAX_THREADS, &maxThreads); }
returnfd;
Okay, so that’s it for Process::self, so what did we do?
L Open the /dev/binder device so that it can interact with the kernel binder mechanism
L Maps FDS to memory, which is expected to be shared with binder devices after device FDS are passed in
Next, it’s time to call defaultServiceManager().
2.2 defaultServiceManager
DefaultServiceManager is located in Framework \ Base \libs\ Binder \ iservicemanager.cpp
sp<IServiceManager>defaultServiceManager()
{
if (gDefaultServiceManager ! = NULL) returngDefaultServiceManager;
// Another singleton, called singleton in the design pattern.
{
AutoMutex_l(gDefaultServiceManagerLock);
if (gDefaultServiceManager == NULL) {
// The real gDefaultServiceManager is created here
gDefaultServiceManager =interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
}
}
return gDefaultServiceManager;
}
— — — — –“
gDefaultServiceManager= interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
ProcessState::self is sure to return the gProcess you just created, and then calls its getContextObject. Note that NULL, 0, is passed in
// Back to the ProcessState class,
sp<IBinder>ProcessState::getContextObject(const sp<IBinder>& caller)
{
If (supportsProcesses()) {// This function determines whether process is supported based on the success of opening the device,
// On a real plane, this is the way to go
return getStrongProxyForHandle(0); // Notice that 0 is passed in here
}
}
—- goes to getStrongProxyForHandle, a function with a weird name that often severely hampers your brain
// Note the name of this parameter, handle. Those of you who have worked with Windows will be familiar with the name, that’s right
// An identifier for a resource, which is essentially a data structure stored in an array, and handle is its index in that array. –> It’s just such a thing
sp<IBinder>ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
handle_entry*e = lookupHandleLocked(handle); – “ha ha, sure enough, from the array to find the corresponding
The indexed resource, lookupHandleLocked, will return a handle_Entry internally
Here is the structure of handle_Entry
/ *
struct handle_entry {
IBinder* binder; —>Binder
RefBase::weakref_type* refs; –> If you don’t know what it is, it doesn’t matter.
};
* /
if (e ! = NULL) {
IBinder* b = e->binder; –> < p style = “max-width: 100%; clear: both
if (b == NULL ||! e->refs->attemptIncWeak(this)) {
b = new BpBinder(handle); –> See, create a new BpBinder
e->binder = b;
result = b;
}…
}
return result; Returns the BpBinder you just created.
}
// Is it a bit confusing here? Yes, it’s easy to forget when your brain is analyzing function calls too deeply.
GDefaultServiceManager = Interface_cast <IServiceManager>(
ProcessState::self()->getContextObject(NULL));
Now, this function call is going to be
gDefaultServiceManager= interface_cast<IServiceManager>(new BpBinder(0));
What is BpBinder? Android has too many names.
Since the larger framework of Binder mechanism has not been introduced, it is not appropriate to introduce BpBinder here, but since we are talking about BpBinder, it seems that it is not clear without introducing Binder architecture…. , sigh!
Well, keep simplifying the deep function call stack, and at least your brain will still work. Let’s take a look at BpBinder’s constructor.
2.3 BpBinder
BpBinder location is in Framework \ Base \libs\binder\ bpbinder.cpp.
BpBinder::BpBinder(int32_thandle)
: mHandle(handle) // Note that 0 is passed in for this call
, mAlive(1)
, mObitsSent(0)
, mObituaries(NULL)
{
IPCThreadState::self()->incWeakHandle(handle); // select * from IPCThreadState;
}
IPCThreadState::self is a singleton.
// The file is located in framework base libs binder ipcThreadState.cpp
IPCThreadState*IPCThreadState::self()
{
If (gHaveTLS) {// first entry is false
restart:
const pthread_key_t k = gTLS;
//TLS is used for Thread Local Storage. All I need to do here is
// What is the advantage of knowing that there is one such space per thread and that threads do not share these Spaces? I don’t have to do anything
// It is synchronized. So in this thread, I’m just going to use this thread’s stuff, because no other thread can get the data in TLS from any other thread. === “this sentence has loopholes, it is ok to understand about the meaning of the point.
// Get the IPCThreadState object stored in thread local storage space
Pthread_getspecific = pthread_getSpecific = pthread_getSpecific = pthread_getSpecific
/ / pthread_setspecific.
IPCThreadState* st =(IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState; // New object,
}
if(gShutdown) return NULL;
pthread_mutex_lock(&gTLSMutex);
if (! gHaveTLS) {
if (pthread_key_create(&gTLS,threadDestructor) ! = 0) {
pthread_mutex_unlock(&gTLSMutex);
return NULL;
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex);
gotorestart; // I FT, actually goto is not as vile as we said, assembly code many jump statements.
// The key is to use it well.
}
Pthread_setspecific (); pthread_setSpecific ()
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),mMyThreadId(androidGetTid())
{
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
//mIn,mOut is Parcel. Think of it as a buffer for commands. Any further explanation, and your brain will shut down again.
mOut.setDataCapacity(256);
}
Come out, finally come out…. Well, back to BpBinder.
BpBinder::BpBinder(int32_thandle)
: mHandle(handle) // Note that 0 is passed in for this call
, mAlive(1)
, mObitsSent(0)
, mObituaries(NULL)
{
.
IPCThreadState::self()->incWeakHandle(handle);
What incWeakHandle, do not talk about..
}
Oh, new BpBinder is done. So what have we created here?
L ProcessState is available.
L IPCThreadState is present and belongs to the main thread.
L BpBinder has an internal handle value of 0
gDefaultServiceManager =interface_cast<IServiceManager>(new BpBinder(0));
We’re back to square one. Are we losing our minds?
Interface_cast, which I first encountered as something like static_cast, and then couldn’t figure out how BpBinder* could be converted to IServiceManager*, Spent NTH more time checking whether BpBinder and IServiceManager inherit or what…. .
Finally, I use CTRL + mouse (Source Insight) to track into interface_cast
IInterface. H is located in the framework/base/include/binder/IInterface. H
template<typenameINTERFACE>
inlinesp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
Therefore, the above is equivalent to:
inline sp<IServiceManager>interface_cast(const sp<IBinder>& obj)
{
return IServiceManager::asInterface(obj);
}
Looks like we have to follow IServiceManager.
IServiceManager. H – “framework/base/include/binder/IServiceManager. H
See how it is defined:
2.4 IServiceManager
classIServiceManager : public IInterface
{
// The ServiceManager manages what? Add services, query services, etc
// Only the addService function is listed here
public:
DECLARE_META_INTERFACE(ServiceManager);
virtual status_t addService( const String16& name,
const sp<IBinder>& service) = 0;
};
DECLARE_META_INTERFACE (ServiceManager)??
How is it so similar to MFC? Microsoft has a lot of influence! If you know MFC, DELCARE must have IMPLEMENT
And sure enough, the two macros, DECLARE_META_INTERFACE and IMPLEMENT_META_INTERFACE(INTERFACE, NAME), are there
I defined it in iinterface.h. What does the DECLARE_META_INTERFACE macro add to IServiceManager?
Here is the DECLARE macro
#defineDECLARE_META_INTERFACE(INTERFACE) \
static const android::String16descriptor; \
static android::sp<I##INTERFACE>asInterface( \
constandroid::sp<android::IBinder>& obj); \
virtual const android::String16&getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##INTERFACE();
We cash it to IServiceManager is:
static constandroid::String16 descriptor; –> Oh, add a description string
staticandroid::sp< IServiceManager > asInterface(constandroid::sp<android::IBinder>&
Obj) — added an asInterface function
virtual constandroid::String16& getInterfaceDescriptor() const; Add a get function
The return value is supposed to be the string descriptor
IServiceManager(); \
virtual ~IServiceManager(); Add construction and virtual decaying functions…
So where is this IMPLEMENT defined?
See IServiceManager. CPP. In the framework/base/libs/binder/IServiceManager CPP
IMPLEMENT_META_INTERFACE(ServiceManager,”android.os.IServiceManager”);
Here is the definition of this macro
#defineIMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
const android::String16I##INTERFACE::descriptor(NAME); \
const android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
android::sp<I##INTERFACE>I##INTERFACE::asInterface( \
constandroid::sp<android::IBinder>& obj) \
{\
android::sp<I##INTERFACE>intr; \
if (obj ! = NULL) { \
intr =static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
I##INTERFACE::descriptor).get()); \
if (intr == NULL) { \
intr = newBp##INTERFACE(obj); \
} \
} \
return intr; \
} \
I##INTERFACE::I##INTERFACE() { } \
I##INTERFACE::~I##INTERFACE(){ } \
Is it a hassle? Hiro, in particular, looks like he has a headache. Just cash it in.
const
Android: : String16IServiceManager: : descriptor (” android. OS. IServiceManager “);
constandroid::String16& IServiceManager::getInterfaceDescriptor() const
{ return IServiceManager::descriptor; / / return above the android OS. IServiceManager
} android::sp<IServiceManager> IServiceManager::asInterface(
constandroid::sp<android::IBinder>& obj)
{
android::sp<IServiceManager>intr;
if (obj ! = NULL) {
intr = static_cast<IServiceManager*>(
obj->queryLocalInterface(IServiceManager::descriptor).get());
if (intr == NULL) {
intr = new BpServiceManager(obj);
}
}
return intr;
}
IServiceManager::IServiceManager () {}
IServiceManager::~ IServiceManager() { }
Interface_cast BpBinder/IServiceManager
Interface_cast > IServiceManager (new BpBinder(0))
AsInterface (New BpBinder(0))
android::sp<IServiceManager>IServiceManager::asInterface(
constandroid::sp<android::IBinder>& obj)
{
android::sp<IServiceManager>intr;
if (obj ! = NULL) {
.
intr = new BpServiceManager(obj);
// God, finally see something related to IServiceManager, look
BpServiceManager(new BpBinder(0));
}
}
return intr;
}
What the hell is BpServiceManager? What does p stand for?
2.5 BpServiceManager
Finally, we can talk about architecture. P is proxy, Bp is BinderProxy, BpServiceManager is THE Binder agent of SM. Since it is a proxy, it must want to be transparent to the user, that is, there is no definition of Bp in the header file. Isn’t it?
Sure enough, BpServiceManager was just defined in iservicemanager.cpp.
classBpServiceManager : public BpInterface<IServiceManager>
// This type of inheritance means that both BpInterface and IServiceManager are inherited, so IServiceManger
AddService must be implemented in this class
{
public:
// Note the impl name of the constructor argument. Is Bridge mode used? Is the IMPL object really done?
Impl newBpBinder(0)
BpServiceManager(constsp<IBinder>& impl)
:BpInterface<IServiceManager>(impl)
{
}
virtual status_t addService(constString16& name, const sp<IBinder>& service)
{
More on that later..
}
Constructor of base class BpInterface (after cashed)
// The remote parameter is remote.
inlineBpInterface< IServiceManager >::BpInterface(const sp<IBinder>&remote)
: BpRefBase(remote)
{
}
BpRefBase::BpRefBase(constsp<IBinder>& o)
: mRemote(o.get()), mRefs(NULL), mState(0)
//o.get(), this is a method of the sp class to get the actual data pointer, just know
// it returns a pointer to sp< XXXX >
{
//mRemote is BpBinder(0)
.
}
Ok, so here we are:
sp<IServiceManager> sm =defaultServiceManager(); The actual return is BpServiceManager, whose remote object is BpBinder, and the handle argument passed in is 0.
Now back to MediaService.
int main(int argc,char** argv)
{
sp<ProcessState>proc(ProcessState::self());
sp<IServiceManager>sm = defaultServiceManager();
// That’s it
MediaPlayerService::instantiate(); // Instantiate MediaPlayerservice
// There is something going on here!
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
At this point, we turn on the Binder device and get a BpServiceManager object, which means we can deal with SM, but we’re not doing anything meaningful, are we?
2.6 MediaPlayerService
So what do we do next? Take MediaPlayerService as an example.
It is located in the framework/base/media/libmediaplayerservice \ libmediaplayerservice CPP
voidMediaPlayerService::instantiate() {
defaultServiceManager()->addService(
// Pass in the name of the service, pass in the object out of new
String16(“media.player”),new MediaPlayerService());
}
MediaPlayerService::MediaPlayerService()
{
LOGV(“MediaPlayerServicecreated”); // Too simple
mNextConnId = 1;
}
DefaultServiceManager returns the BpServiceManager you just created
Call its addService function.
MediaPlayerService is derived from BnMediaPlayerService
classMediaPlayerService : public BnMediaPlayerService
FT MediaPlayerService derived from BnMediaPlayerService BnXXX BpXXX
Bn is the meaning of Binder Native, as opposed to Bp, and THE P of Bp means proxy. Then there must be something on the other end that deals with proxy, and this is Bn.
This is going to be a little confusing. Let’s analyze what we’ve constructed so far.
l BpServiceManager
l BnMediaPlayerService
The two objects are not at opposite ends. From BnXXX, it can be inferred that BpServiceManager corresponds to BnServiceManager, and BnMediaPlayerService corresponds to BpMediaPlayerService.
Where are we now? We have now created the BnMediaPlayerService and want to add it to the system.
Oh, I see. I create a new service-bnMediaPlayerService-and want to tell it to the ServiceManager.
So how do I communicate with the ServiceManager? Well, use BpServiceManager. So, I called BpServiceManager addService!
Why have a ServiceManager? This has to do with Android mechanics. All services must be added to the ServiceManager to manage them. It also makes it easier for the Client to check which services exist in the system. Didn’t you see the string we passed in? This makes it possible to find the Service using the Human Readable string.
I don’t feel clear… Forgive me.
2.7 the addService
AddService is the function called BpServiceManager. I didn’t get to that, but now let’s see.
virtual status_taddService(const String16& name, const sp<IBinder>& service)
{
Parcel data, reply;
// Data is the command package sent to BnServiceManager
// See? First written into, the Interface name. What is the android OS. IServiceManager
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
// Add the name of the new service called media.player
data.writeString16(name);
// Write the new service service — > MediaPlayerService to the command
data.writeStrongBinder(service);
// Call remote’s transact function
status_t err =remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readInt32() : err;
}
Omg, what does remote() return?
remote(){ return mRemote; } — >? Can not find the corresponding actual object?
Remember what we said when we initialized:
This parameter is also called remote.
MRemote was originally created as BpBinder..
Well, go there and check it out:
The location of BpBinder is framework\base\libs\binder\ bpbinder.cpp
status_tBpBinder::transact(
uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{
// Call transact of IPCThreadState.
// mHandle is 0,code is ADD_SERVICE_TRANSACTION, and data is the command package
// Reply is the reply package, flags=0
status_t status =IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
.
}
Take a look at the transact function for IPCThreadState
status_tIPCThreadState::transact(int32_t handle,
uint32_tcode, const Parcel& data,
Parcel* reply,uint32_t flags)
{
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
if (err == NO_ERROR) {
// Call writeTransactionData to send data
err = writeTransactionData(BC_TRANSACTION, flags,handle, code, data, NULL);
}
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err =waitForResponse(&fakeReply);
}
. Reply etc.
err = waitForResponse(NULL, NULL);
.
return err;
}
Take it one step further and look at this…
status_tIPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, constParcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
tr.target.handle = handle;
tr.code = code;
tr.flags = binderFlags;
const status_t err = data.errorCheck();
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();
}
.
The command data is encapsulated as binder_transaction_data, and then
Write to mOut, which is a command buffer and Parcel
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
// Add Parcel to /dev/binder.
Well, that has to be written to binder devices in another place. Is it in?
return NO_ERROR;
}
// Yes, in waitForResponse
status_tIPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while(1) {
//talkWithDriver, ha ha, should be here
if ((err=talkWithDriver()) <NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
// See? So here I’m doing mIn, so it looks like in talkWithDriver
// Send out the mOut and read the data from the driver into mIn.
cmd = mIn.readInt32();
switch (cmd) {
caseBR_TRANSACTION_COMPLETE:
if (! reply &&! acquireResult) goto finish;
break;
.
return err;
}
status_tIPCThreadState::talkWithDriver(bool doReceive)
{
binder_write_read bwr;
// mOut data and mIn received data are assigned to BWR.
status_t err;
do {
// Use ioctl to read and write
if (ioctl(mProcess->mDriverFD,BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
} while (err == -EINTR);
// At this point, the reply data is in the BWR, and the buffer that BMR receives the reply data is provided by mIn
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
returnNO_ERROR;
}
Well, at this point, we’re done sending addService.
BpServiceManager sends an addService command to BnServiceManager and receives a reply.
Let’s continue with our main function.
int main(int argc,char** argv)
{
sp<ProcessState>proc(ProcessState::self());
sp<IServiceManager> sm =defaultServiceManager();
MediaPlayerService::instantiate();
This function internally calls addService to add MediaPlayerService information to the ServiceManager
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
Here’s where it’s easy to get confused:
MediaPlayerService is a BnMediaPlayerService, so it should not wait
BpMediaPlayerService to interact with him? We don’t see MediaPlayerService opening binder devices.
BnServiceManager will continue with addService
BnMediaPlayerService?
Let’s start with BnServiceManager. By the way, the Binder architecture of the system.
2.8 BnServiceManager
As mentioned above, defaultServiceManager returns a BpServiceManager through which command requests can be sent to binder devices and handle has a value of 0. Well, there must be someone on the other end of the system receiving orders. Who is that?
Unfortunately, BnServiceManager does not exist, but there is a program that does BnServiceManager’s job: service.exe. Exe means it’s a program.
Location in the framework/base/CMDS servicemanger. C.
int main(int argc,char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024); // Open binder devices.
Binder_become_context_manager (bs) // Become manager
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler); // Process the command sent by BpServiceManager
}
See if binder_open is as good as we guessed?
struct binder_state*binder_open(unsigned mapsize)
{
struct binder_state *bs;
bs = malloc(sizeof(*bs));
.
bs->fd = open(“/dev/binder”,O_RDWR); // Sure enough
.
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize,PROT_READ, MAP_PRIVATE, bs->fd, 0);
}
Look again at binder_become_context_manager
intbinder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd,BINDER_SET_CONTEXT_MGR, 0); // Set yourself to MANAGER
}
Binder_loop must be a loop that reads requests and writes replies from binder devices, right?
voidbinder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf,sizeof(unsigned));
for (;;) {// It’s a loop
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
// I received the request
res = binder_parse(bs, 0, readbuf,bwr.read_consumed, func);
}
This… More to say later?
Well, finally, there’s a handleMessage-like place to handle all kinds of commands. This is the
Svcmgr_handler, which is in Servicemanager.c
intsvcmgr_handler(struct binder_state *bs,
struct binder_txn *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
unsigned len;
void *ptr;
s = bio_get_string16(msg, &len);
switch(txn->code) {
case SVC_MGR_ADD_SERVICE:
s = bio_get_string16(msg, &len);
ptr = bio_get_ref(msg);
if (do_add_service(bs, s, len, ptr,txn->sender_euid))
return -1;
break;
.
Where do_add_service actually adds BnMediaService information
intdo_add_service(struct binder_state *bs,
uint16_t *s, unsigned len,
void *ptr, unsigned uid)
{
struct svcinfo *si;
si = find_svc(s, len); S is a list
si = malloc(sizeof(*si) + (len + 1) *sizeof(uint16_t));
si->ptr = ptr;
si->len = len;
memcpy(si->name, s, (len + 1) *sizeof(uint16_t));
si->name[len] = ‘\0’;
si->death.func = svcinfo_death;
si->death.ptr = si;
si->next = svclist;
svclist = si; // See, the svclist is a list that holds the current registration to the ServiceManager
The information in the
}
binder_acquire(bs, ptr); // This one. When this Service exits, I want the system to notify me so that I can release resources from malloc. That’s probably what they do.
binder_link_to_death(bs, ptr,&si->death);
return 0;
}
Well, as far as addService is concerned, it looks like the ServiceManager adds the information to a list of services it maintains.
2.9 Significance of the ServiceManager
Why do you need something like this?
In Android, the Service information is added to the ServiceManager. The ServiceManager manages the Service information in a centralized manner. In this way, you can query the services available in the system. If a client of an Android service such as MediaPlayerService wants to communicate with MediaPlayerService, it must first query the MediaPlayerService with the ServiceManager. The ServiceManager then interacts with the MediaPlayerService through what it returns.
After all, if the MediaPlayerService is in bad health and keeps failing, the client code will be in trouble and will not know about the subsequent new MediaPlayerService, so this is what happens:
L MediaPlayerService registers with SM
L MediaPlayerClient Queries information about the MediaPlayerService currently registered in SM
L Based on this information, MediaPlayerClient interacts with MediaPlayerService
In addition, the Handle identifier of the ServiceManager is 0, so any message sent to a service with a Handle of 0 will eventually be passed to the ServiceManager.
The operation of MediaService
In the last section, we learned:
L defaultServiceManager gets BpServiceManager, then MediaPlayerService instantiates and calls BpServiceManager’s addService function
In this process, it is the Service_manager that receives the addService request and puts the corresponding information into its own service list
Here, we can see that service_manager has a binder_looper function waiting to receive requests from binder. Although Service_manager is not derived from BnServiceManager, it certainly does what BnServiceManager does.
Similarly, we created MediaPlayerService (BnMediaPlayerService), which should also:
L Open binder equipment
Do a Looper loop, too, and wait for the request
Service, service, this is similar to network programming to listen to socket work.
Well, since the MediaPlayerService constructor doesn’t see the binder device being opened, what does its parent, BnXXX, do?
3.1 MediaPlayerService Enable Binder
classMediaPlayerService : public BnMediaPlayerService
//MediaPlayerService is derived from BnMediaPlayerService
BnMediaPlayerService is derived from both BnInterface and IMediaPlayerService
classBnMediaPlayerService: public BnInterface<IMediaPlayerService>
{
public:
virtual status_t onTransact( uint32_t code,
constParcel& data,
Parcel*reply,
uint32_t flags= 0);
};
BnInterface seems to be more about opening devices.
template<typenameINTERFACE>
class BnInterface :public INTERFACE, public BBinder
{
public:
virtual sp<IInterface> queryLocalInterface(const String16&_descriptor);
virtual const String16& getInterfaceDescriptor() const;
protected:
virtual IBinder* onAsBinder();
};
Cashed into
class BnInterface :public IMediaPlayerService, public BBinder
BBinder? BpBinder? BnXXX = BnXXX = BpXXX If so, why is it called BBinder?
BBinder::BBinder()
: mExtras(NULL)
{
// Where is the device not turned on?
}
Finished? Are we going in the wrong direction? Doesn’t every Service have a corresponding BINDER device FD?
.
Think back to our Main_MediaService app. Did any of binder open?
int main(int argc,char** argv)
{
// don’t I have binder open in ProcessState?
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm =defaultServiceManager();
MediaPlayerService::instantiate();
.
3.2 which
Ah? The opening of binder devices is process related. Open one for each process. So where do I do a similar message loop looper operation?
.
// Is it the following two?
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
Have a look at startThreadPool
voidProcessState::startThreadPool()
{
.
spawnPooledThread(true);
}
voidProcessState::spawnPooledThread(bool isMain)
{
sp<Thread> t = newPoolThread(isMain); IsMain is TRUE
// Create a Thread pool and run it.
t->run(buf);
}
PoolThread is derived from the Thread class, so is a Thread generated at this point? Take a look at PoolThread and Thread construction
PoolThread::PoolThread(boolisMain)
: mIsMain(isMain)
{
}
Thread::Thread(boolcanCallJava)//canCallJava Default value is true
: mCanCallJava(canCallJava),
mThread(thread_id_t(-1)),
mLock(“Thread::mLock”),
mStatus(NO_ERROR),
mExitPending(false), mRunning(false)
{
}
Oh, no thread has been created yet. PoolThread::run is then called, actually calling the run of the base class.
status_tThread::run(const char* name, int32_t priority, size_t stack)
{
bool res;
if (mCanCallJava) {
Res = createThreadEtc(_threadLoop,// thread function is _threadLoop
this, name, priority, stack,&mThread);
}
// Finally, in the run function, the thread is created. From now on
Main thread execution
IPCThreadState::self()->joinThreadPool();
The newly opened thread executes _threadLoop
Let’s look at _threadLoop first
intThread::_threadLoop(void* user)
{
Thread* const self =static_cast<Thread*>(user);
sp<Thread>strong(self->mHoldSelf);
wp<Thread> weak(strong);
self->mHoldSelf.clear();
do {
.
if (result && ! self->mExitPending){
result = self->threadLoop(); Wow, call your own threadLoop
}
}
We are PoolThread objects, so call the PoolThread threadLoop function
virtualbool PoolThread ::threadLoop()
{
/ / mIsMain to true.
// Also note that this is a new thread, so one must be created
The new IPCThreadState object (remember thread-local storage? TLS) and then
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
Both the main thread and the worker thread called joinThreadPool.
voidIPCThreadState::joinThreadPool(bool isMain)
{
mOut.writeInt32(isMain ? BC_ENTER_LOOPER :BC_REGISTER_LOOPER);
status_t result;
do {
int32_t cmd;
result = talkWithDriver();
result = executeCommand(cmd);
}
} while (result ! = -ECONNREFUSED&& result ! = -EBADF);
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}
See yet? There is a loop, but it looks like two threads are executing it! There are two message loops, right?
Now look at executeCommand
status_tIPCThreadState::executeCommand(int32_t cmd)
{
BBinder*obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
caseBR_TRANSACTION:
{
binder_transaction_data tr;
result = mIn.read(&tr,sizeof(tr));
// a command comes in, parses into BR_TRANSACTION, and reads the subsequent information
Parcel reply;
if (tr.target.ptr) {
// Use BBinder.
sp<BBinder>b((BBinder*)tr.cookie);
const status_t error =b->transact(tr.code, buffer, &reply, 0);
}
Let’s see what BBinder’s transact function does
status_tBBinder::transact(
uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{
Call your own onTransact function
err= onTransact(code, data, reply, flags);
return err;
}
BnMediaPlayerService is derived from BBinder, so its onTransact function is called
Finally, let’s look at BnMediaPlayerServcice’s onTransact function.
status_tBnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{
BnMediaPlayerService is derived from BBinder and IMediaPlayerService, all iMediaPlayerServices
// See the switch below? All functions provided by IMediaPlayerService are distinguished by command types
//
switch(code) {
case CREATE_URL: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
Create is a virtual function implemented by MediaPlayerService!!
sp<IMediaPlayer> player = create(
pid, client, url,numHeaders > 0 ? &headers : NULL);
reply->writeStrongBinder(player->asBinder());
return NO_ERROR;
} break;
In fact, at this point, we understand. The onTransact function of BnXXX takes the command and sends it to the functions of derived classes, who do the actual work.
Description:
There are two threads, the main thread and the worker thread, after startThreadPool and joinThreadPool are done, and both are doing message loops. Why do you do that? They both take isMain to be true. I don’t know what Google is doing. Are you afraid that one thread is too much work, so you have two threads to work on? This explanation should also be reasonable.
Someone on the Internet has tested that the last sentence is blocked, and it works. But if the main thread is raised, can the program not exit? This… Whatever, just know that there are two threads working on it.
Four MediaPlayerClient
This is how the MediaPlayerClient interacts with the MediaPlayerService.
To use MediaPlayerService, create its BpMediaPlayerService. Let’s look at an example
IMediaDeathNotifier::getMediaPlayerService()
{
sp<IServiceManager> sm =defaultServiceManager();
sp<IBinder> binder;
do {
// Query SM for service information, return binder
binder =sm->getService(String16(“media.player”));
if (binder ! = 0) {
break;
}
usleep(500000); / / 0.5 s
} while(true);
// Convert this binder to BpMediaPlayerService via interface_cast
// Note that binder is only used to communicate with binder devices
// has nothing to do with the functionality of IMediaPlayerService.
// Remember what I said about Bridge mode? BpMediaPlayerService uses this binder and BnMediaPlayerService
/ / communications.
sMediaPlayerService =interface_cast<IMediaPlayerService>(binder);
}
return sMediaPlayerService;
}
Why the Bridge? It’s not necessarily Bridge mode, but what I really want to say is:
A Binder is simply an interface to a Binder device, and the upper-layer IMediaPlayerService uses it as a socket. I used to confuse binder with the functionality of the upper class IMediaPlayerService.
Of course, you don’t have to make that mistake. But there is one caveat:
4.1 Native layer
The getMediaPlayerService code is C++ layer, but the entire example used is a JAVA->JNI layer call. What if I wanted to write a pure C++ program?
int main()
{
getMediaPlayerService(); Can I get BpMediaPlayerService by calling this function directly?
No, why not? Because I haven’t turned on the Binder drive yet! But you have Google already doing it for you in your JAVA application
It’s packaged.
Therefore, pure native layer code must also be treated like this:
sp<ProcessState>proc(ProcessState::self()); // This is not necessary because
// This is required in many places, so it will be created automatically.
getMediaPlayerService();
You have to have a message loop, otherwise if there is a message from Bn, how can you receive it?
ProcessState::self()->startThreadPool();
// Whether the main thread also calls the message loop depends on the individual. Instead, you wait to receive messages from other sources, such as socket commands, and then control MediaPlayerService.
}
Implement your own Service
With binders, how do you implement your own Service?
If it were a pure C++ program, it would have to act like main_MediaService.
int main()
{
sp<ProcessState>proc(ProcessState::self());
sp<IServiceManager>sm = defaultServiceManager();
Sm – > the addService (” service. The name “newXXXService ());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
How to define XXXService?
We need a Bn, we need a Bp, and the Bp doesn’t have to be exposed. So implement it together in bnxxx.cpp.
In addition, XXXService provides its own functionality, such as getXXX calls
5.1 Defining the XXX Interface
The XXX interface is related to the XXX service, such as providing getXXX, setXXX functions, and application logic.
Needs to be derived from IInterface
class IXXX: publicIInterface
{
public:
DECLARE_META_INTERFACE(XXX); ShenMingHong
virtualgetXXX() = 0;
virtualsetXXX() = 0;
} This is an interface.
5.2 Define BnXXX and BpXXX
To add IXXX to the Binder structure, you need to define BnXXX and CLIENT-transparent BpXXX.
BnXXX requires a header file. BnXXX simply adds the IXXX interface to the Binder architecture without participating in the actual getXXX and setXXX application-layer logic.
This BnXXX definition can be placed alongside the IXXX definition above. We can split up.
class BnXXX: publicBnInterface<IXXX>
{
public:
virtual status_t onTransact( uint32_t code,
constParcel& data,
Parcel*reply,
uint32_tflags = 0);
// since IXXX is a pure virtual class, BnXXX implements only onTransact, so BnXXX is still a virtual class
A pure virtual class
};
So, given DECLARE, let’s IMPLEMNT it in a CPP. In ixxx. CPP.
IMPLEMENT_META_INTERFACE(XXX,”android.xxx.IXXX”); / / IMPLEMENT macro
status_t BnXXX::onTransact(
uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{
switch(code) {
case GET_XXX: {
CHECK_INTERFACE(IXXX, data, reply);
Read request parameters
Call the virtual function getXXX()
return NO_ERROR;
} break; / / SET_XXX similar
BpXXX is also implemented here.
class BpXXX: publicBpInterface<IXXX>
{
public:
BpXXX(const sp<IBinder>& impl)
: BpInterface< IXXX >(impl)
{
}
vituralgetXXX()
{
Parcel data, reply;
data.writeInterfaceToken(IXXX::getInterfaceDescriptor());
data.writeInt32(pid);
remote()->transact(GET_XXX, data,&reply);
return;
}
/ / setXXX similar
At this point, Binder should be able to do the following after the analysis:
If you need to write your own Service, you should always know how the system calls your function. right There are two threads out there constantly taking commands from binder devices and calling your functions. Well, this is a multithreading problem.
If you need to track bugs, you need to know how functions called from the Client end up in the remote Service. Thus, for some function calls, when the Client is done tracing, I know to go to Service to see the corresponding function call. It’s synchronous anyway. The Client waits for a function call until the Service returns.