preface

This story is pure fiction, if there is any illogical please spray. ❤ ️

Inuyasha 2021

Chapter 1: Will I ever find you again, Ari

After the Armageddon between Inuyasha and Naraku, the jade of four souls and the well of Eating bones disappear, and no one knows where they went, while Inuyasha and Agari are again separated into two worlds.

So Inuyasha asks a master who studies the universe, Block, to help him find Ahli.

Time to 2021, blocks finally discovered the secret of the world.

In fact, the entire universe we live in is called the Android Universe, and the two worlds inuyasha and Agari live in are actually two processes, and the two worlds can be connected through the bone-eating well.

So if you want inuyasha to reconnect with Ahli, you have to find the bone-eating well again.

Chapter 2: Bone-eating Wells renamed Binder Wells?

“Inuyasha, I finally found it.”

“What have you found? Is that Li? A hedge found ????”

“No, but I found the key — the bone-eating well.”

“Where is it? Take me there.”

So The Block master took Inuyasha to a room:

Inuyasha flew into the room, which had the words “Kernel House” written on its front, and found the bone-eating well, but with a different name: Binder Well, on a broken board next to it. The board is also engraved with instructions:

The Binder well connects these two worlds. What you see may not be real. Use with caution! To use it, find the lost Jade of the Four Souls, now called ServiceManager, find the jade of SM, and mentally talk about the person you want to contact in that world. If she leaves an address in the jade of SM in that world, then you will find her.

“Block master, do you know the jade of SM and where to find it?” inuyasha asks.

Chapter three: The Jade of the Four Souls – ServiceManager

“When it comes to the jade of SM, it goes back to the origin of the universe. When the Android universe was created, there was the first human world (user process) called the Init world, and the jade of SM was created from that world.

When SM Jade was created, he started the Binder well and became his patron saint.

But its true self exists in a separate world and cannot be obtained. In order for people to use it, it purposely left its own pieces (proxies) in each world.”

“Tell me where it is.”

“Street 0 (handle fixed to 0),” the Block mage said, pointing in one direction.

Chapter four: Li, I miss you

Inuyasha hurried to Street 0, found the piece of SM’s jade, and then went back to the Binder well and said to herself:

“SM jade, please help me find a hedge.”

Suddenly, a gust of wind blew from the Binder well and a phantom appeared in front of Inuyasha.

Is the fence ~

“Li, can you hear me?”

“Inuyasha, I can hear you, I didn’t expect to see you,” said the shadow of A li.

“I missed you, Li…”

The story End

End of story.

Binder’s workflow:

  • Ari (server) leaves the address of their world (process) on ServiceManager in order for Inuyasha (client) to find her.
  • Inuyasha finds the Jade Shard of the Four Souls (ServiceManager Agent) on Street 0 (handle 0).
  • Through the jade Shards of the Four Souls, Inuyasha sees the virtual shadow of Ahli (server agent) and tells ahli through the virtual shadow that she misses her (correspondence).

Of course, a story is a story, and it’s not entirely clear.

The following is a complete look at Binder’s workflow and principles

Code to achieve inuyasha’s needs

First, we used AIDL to implement the scene in the story — to get Inuyasha and Ahri to talk to each other in different stages:

//IMsgManager.aidl

interface IMsgManager {
      String getMsg(a);
      void tell(in String msg);
}

/ / the hedge
public class AliWorldService extends Service {

    private static final String TAG = "lz1";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private Binder mBinder = new IMsgManager.Stub() {
        @Override
        public String getMsg(a) throws RemoteException {
            String tellMsg="Inuyasha... Is me";
            Log.e(TAG, "Ah Li:" + tellMsg);
            return tellMsg;
        }

        @Override
        public void tell(String msg) throws RemoteException {
            Log.e(TAG, "I'm Li. I got what you said :"+ msg); }}; } <service android:name=".binder.AliWorldService"
    android:process=":aliworld">
</service>


/ / inuyasha
public class QycWorldActivity extends Activity {
    private static final String TAG = "lz1";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qyc);

        Intent i = new Intent(this, AliWorldService.class);
        bindService(i, mConnection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IMsgManager msgManager = IMsgManager.Stub.asInterface(service);
            try {
                String tellMsg="Hi, is that you?";
                Log.e(TAG, "Inuyasha:" + tellMsg);
                msgManager.tell(tellMsg);


                String msg = msgManager.getMsg();
                Log.e(TAG, "I am Inuyasha, and I received what you said:" + msg);

            } catch(RemoteException e) { e.printStackTrace(); }}@Override
        public void onServiceDisconnected(ComponentName name) {}};@Override
    protected void onDestroy(a) {
        super.onDestroy(); unbindService(mConnection); }}Copy the code

Run, print the result:

E/ Lz1: Inuyasha: Ari, is that you E/ Lz1: Ari, is that you E/ Lz1: Ari: Inuyasha... It's me E/ LZ1: I am Inuyasha, I received what you said: Inuyasha... Is myCopy the code

AIDL principle

The code is relatively simple. The server side creates a Binder object and transmits it to the onBind method. After the client side bindService obtains the proxy interface of the server side, the method can be called.

AIDL is actually a tool for interprocess communication. It generates Java interface code based on AIDL files that we write, which is also internally implemented with Binder.

We can find the interface class generated by AIDL by building — generated — aidl_source_output_dir — debug — out. The code is as follows:

public interface IMsgManager extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.example.studynote.binder.IMsgManager {

        / / 1
        private static final java.lang.String DESCRIPTOR = "com.example.studynote.binder.IMsgManager";

        public Stub(a) {
            this.attachInterface(this, DESCRIPTOR);
        }

        / / 2
        public static com.example.studynote.binder.IMsgManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if(((iin ! =null) && (iin instanceof com.example.studynote.binder.IMsgManager))) {
                return ((com.example.studynote.binder.IMsgManager) iin);
            }
            return new com.example.studynote.binder.IMsgManager.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder(a) {
            return this;
        }


        / / 4
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getMsg: {
                    data.enforceInterface(descriptor);
                    java.lang.String _result = this.getMsg();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_tell: {
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.tell(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags); }}}private static class Proxy implements com.example.studynote.binder.IMsgManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder(a) {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor(a) {
                return DESCRIPTOR;
            }


            / / 3
            @Override
            public java.lang.String getMsg(a) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getMsg, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void tell(java.lang.String msg) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(msg);
                    mRemote.transact(Stub.TRANSACTION_tell, _data, _reply, 0);
                    _reply.readException();
                } finally{ _reply.recycle(); _data.recycle(); }}}static final int TRANSACTION_getMsg = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_tell = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.lang.String getMsg(a) throws android.os.RemoteException;

    public void tell(java.lang.String msg) throws android.os.RemoteException;
}

Copy the code

The code is quite long, so let’s analyze it in turn:

  • DESCRIPTOR. Unique identification of Binder.

The Stub class constructor binds the current Binder to this unique identifier using the attachInterface method.

  • asInterface(). Convert Binder objects on the server side to interface type objects required by the client side.

This method is called by the client, and in this method, you pass in a unique identifier to get the corresponding Binder through the queryLocalInterface(DESCRIPTOR) method. If the server and client are in the same process, the Binder object on the server, which is the Stub object itself, is returned, and the object’s methods are called directly. If it is in a different process, which is our normal cross-process case, the wrapped stub.proxy Proxy object is returned.

  • Proxy.getMsg/tell

Then we’ll look at the methods in the proxy class, which we actually call in the client Activity.

Two of the more important objects are the _data object and the _reply object, both of type Parcel, where the data is serialized so that it can be transferred across processes.

If the method passes parameters, the parameters are written to the _data object and the Transact method is called to initiate a remote call request (RPC) while the current thread hangs waiting for the server to finish executing the request.

    mRemote.transact(Stub.TRANSACTION_getMsg, _data, _reply, 0);
Copy the code

As you can see, a code of type int — TRANSACTION_getMsg — is passed in to determine which method to call.

After the request ends, the current thread continues, fetching the return result from the _reply object.

The whole IPC process ends. So where does the server actually perform its tasks? Let’s move on to the onTransact method.

  • onTransact

The onTransact method is what the server does, running in the server’s Binder thread pool.

When the client makes a remote call request, it is encapsulated by the Binder driver at the kernel layer and handed to the onTransact method on the server.

In this method, code first knows which method it is, then executes the target method, writes the serialized result to reply, and the RPC process ends and is handed over to the client.

case TRANSACTION_getMsg: {
                    data.enforceInterface(descriptor);
                    java.lang.String _result = this.getMsg();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
Copy the code

Finally draw a diagram to summarize the whole process of AIDL:

Binder

Binder: Binder Binder: Binder Binder: Binder Binder

At the Java level, a Binder is a class that implements the IBinder interface.

The real cross-process part is when the client initiates the remote call request, the system bottom layer is wrapped and handed over to the server. And this system bottom layer encapsulation, in fact, is happening in the Linux kernel.

Binder does this communication in the kernel, not Binder classes, but Binder drivers.

A driver you can think of as a hardware interface that helps the operating system control hardware devices.

Binder drivers are added to run in the Linux kernel space so that two different processes can access the kernel space to exchange data: pass the data to the Binder driver, process it and then hand it back to the other process for cross-process communication.

And just through the AIDL example, we can know the client requests to the server communication, not directly contact with an object from the server, but with the help of an agent from the server object, based on the proxy object, and then the proxy class will transmit the method corresponding code, serialization of the data, need to return to the serialization of the data to the ground floor, Binder drivers. (This also explains why Inuyasha saw a phantom of Ahli, not her real body 😝)

The Binder driver then delivers the data to the server and returns it to the client when the results are calculated.

ServiceManager

At this point, there may be friends who will notice that something is wrong. Didn’t you just say there is a ServiceManager? Here is AIDL communication why not, missed ah?

ServiceManager is actually a mechanism set to manage system services. Each service is registered in ServiceManager and centrally managed by ServiceManager. We can query the corresponding service proxy in ServiceManager by service name. Thus complete the function of invoking system services. A ServiceManager is similar to a DNS in that it records service names and specific services for clients to find.

In our AIDL case, we can get the Service directly from the server, so we can get the proxy class IMsgManager directly from the server, so there is no need to find the Service through the ServiceManager layer.

And the ServiceManager itself also runs in a separate process, so it is also a server, the client actually obtains the ServiceManager proxy object through the cross-process, and then uses the ServiceManager proxy object to find the corresponding service.

A ServiceManager, like a Service in the AIDL, can be found directly. Its handle is always 0, and it is a “well-known” handle. So each APP can create a ServiceManager proxy object in its own process space through the Binder mechanism. (This also explains why the jade of the Four Souls is in another world, in a different process, and we can only find the fragment of the jade of the Four souls with a handle value of 0, which is actually the agent 😝)

So the process of finding system services and invoking methods through the ServiceManager is two cross-process communications.

APP process — >ServiceManager process — > System service process (such as AactivityManagerService)

Let’s take ActivityManagerService as an example to see how ServeiceManager gets system services.

Example of system service communication

Familiar with the process of APP start friends all know that startActivityForResult method will turn to mInstrumentation. ExecStartActivity method, and here for AMS service process with the Binder mechanism:


//mInstrumentation.execStartActivity
    intresult = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target ! =null ? target.mEmbeddedID : null,
                        requestCode, 0.null, options);
    checkStartActivityResult(result, intent);


    public static IActivityManager getService(a) {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create(a) {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    returnam; }};Copy the code

Basically see this sentence: ServiceManager. GetService (Context. ACTIVITY_SERVICE)

The ServiceManager, as we understood, passes in a name — context.activity_service, and then gets the service’s proxy class, the IBinder object, Here is the corresponding AMS proxy object, IActivityManager.

You can then perform a series of operations on AMS.

Here the AMS service actually corresponds to the server, and the party we call, namely the process of APP itself, serves as the client.

The remaining question is, when did AMS register with a ServiceManager? The answer is in SystemServer. As mentioned in the previous article, AMS is started in SystemServer, and is also registered in ServiceManager. Here is the AMS startup and registration service code. For those unfamiliar, check out the SystemServer source code or my previous article.

    //SystemServer.java
    private void startBootstrapServices(a) {
        / /...

        / / start the AMS
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);


        // Set up the application instance for the system process and start it.
        mActivityManagerService.setSystemProcess();
    }

    public void setSystemProcess(a) {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this./* allowIsolated= */ true, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO); }}Copy the code

Here, the complete Binder process is also introduced, and a flow chart of Binder mechanism is added:

conclusion

So what exactly is a Binder? I think you already have the answer in mind, here is a summary of the content of the book “Android development art Exploration”, I hope you have a good aftertaste ~

Intuitively, a Binder is a class that implements the IBinder interface. Binder is a cross-process communication method in Android from an IPC perspective. Also known as a virtual physical device, its device driver is /dev/binder. From an Android FrameWork perspective, Binder is the bridge between ServiceManager managers (ActivityManager, WindowManager, etc.) and responsive ManagerServices. At the Android application layer, Binder is the medium through which clients and servers communicate.Copy the code

Android Architecture

Mind map links

reference

Android Interprocess Communication (IPC) Binder Introduction to Android Interprocess Communication (IPC) Service Manager

Bye bye

Thank you for your reading. If you study with me, you can pay attention to my public account — building blocks on the code ❤️❤️, a knowledge point every day to establish a complete knowledge system architecture. Here is a group of Android friends, welcome to join us