Basic analysis of Android-Binder and AIDL

If you want to learn Binder in Android, IPC (Inter-Process Communication Mechanism) cannot be avoided. Inter-process communication, as the name implies, is the process of passing data from one process to another. This involves the division of memory space in the Linux process. as the picture shows:

Understanding of Binder communication mechanism

Traditional IPC is equivalent to in life, we send express delivery from Beijing to Shanghai. At this time, users in Beijing need to deliver the courier to the courier. For the courier, it is copy_from_user(), and then the courier delivers the courier to the user (receiver) in Shanghai through the transportation of the courier company. For the courier, it is copy_to_user(). This process is equivalent to copying the data twice in the program. The Binder communication mechanism is equivalent to that the courier we send is the Shanghai courier, which saves the process of delivering the courier to the user (receiver) in Shanghai and saves the copy_to_user. This is faster than traditional IPC. Binder maps data to the shared kernel space through mmap mapping mechanism to achieve the purpose of reducing one data copy. Let's use a table to compare the advantages and disadvantages of Binder and traditional IPC.

 

Binder

Shared memory

Socket

performance

Need to copy once

No need to copy

Need to copy twice

Features

Based on C/S architecture

High ease of use

Complex control , poor ease of use

Based on C/S architecture

As a general interface, its transmission efficiency is low and the overhead is high

safety

Assign UID to each APP

Support both real name and anonymity

Rely on upper layer protocol

Access point is open

Not safe

Rely on upper layer protocol

Access point is open

Not safe

Security: Shared memory and Socket rely on upper-layer protocols, they are insecure, and Binder is relatively secure.

Understanding: Shared memory and Socket are equivalent to tying the data to be transmitted including PID and UID into a data packet and transmitting it to the system. At this time, the PID received by the system is passed by the APP and not obtained by the system itself through its own mechanism. There is no ability to distinguish the authenticity. This is equivalent to taking a forged ID card to do things in the society. We know that the majority of people in society generally cannot identify the authenticity of the ID card, which will cause great security risks. In the Binder mechanism, the system will assign PID and UID to each APP (this is equivalent to the public security bureau setting and issuing an ID card for each citizen), which is like going to the public security with a fake ID The bureau commits a crime, can't this be caught on the spot? Therefore, the security of the Binder mechanism is guaranteed.

Summary of Android-Binder operating mechanism

Binder is a class in Android that implements the IBinder interface. From an IPC perspective, Binder is an inter-process communication mechanism in Android. From the perspective of Android FrameWork, Binder is the bridge between ServiceManager to connect various Managers (ActivityManager, WindowManager, etc.) and the corresponding ManagerService; from the Android application, Binder is the communication medium between the client and the server, when bindService , The server will return a Binder object that contains the service calls of the server. Through this Binder object, the client can obtain the services or data provided by the server. The services here include ordinary services and AIDL-based services. In Android development, Binder is mainly used in Service.

First of all, let’s take a look at the Binder operating mechanism diagram. The diagram is a bit ugly drawn by myself, please don’t take it off

As mentioned above, Binder is an inter-process communication mechanism. In Android, we often use AIDL to complete cross-process communication. Now we come to understand the calling process of AIDL. First we think about three questions:

1. How does the client get the AIDL remote control?

2. How to call the server through this remote control?

3. How does the server handle it?

With these three questions, we will analyze the AIDL workflow.

1. Create a new Book class in Android-studio, and implement Android's own serialization interface Parcelable, and complete its steps


public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {

        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}

2. Create a new Book.aidl file

only need to

parcelable Book;

This means that Book.aidl is the declaration of the Book class in AIDL.

3. Create a new IBookManager.aidl file

// 引用一定要写,即使是在同一包下
import com.binderstudydemo.Book;
// Declare any non-default types here with import statements

interface IBookManager {
    //用于从远程服务端获取图书列表
    List<Book> getBookList();
    //用于往图书列表中添加一本书
    void addBook(in Book book);
}

4. The system will generate an IBookManager.java class for us, the code is as follows

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\AndroidSources\\androidStudyDemo\\BinderStudyDemo\\app\\src\\main\\aidl\\com\\binderstudydemo\\IBookManager.aidl
 */
package com.binderstudydemo;
// Declare any non-default types here with import statements

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.binderstudydemo.IBookManager {
        //Binder的唯一标识
        private static final java.lang.String DESCRIPTOR = "com.binderstudydemo.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            // 注释1
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.binderstudydemo.IBookManager interface,
         * generating a proxy if needed.
         */
        /**
         * 用于将服务端的Binder对象转换成客户端所需要的AIDL接口类型的对象,这种转换过程是区分进程的,
         * 如果客户端和服务端在同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy
         */
        public static com.binderstudydemo.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            // 注释2
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.binderstudydemo.IBookManager))) {
                return ((com.binderstudydemo.IBookManager) iin);
            }
            return new com.binderstudydemo.IBookManager.Stub.Proxy(obj);
        }

        /**
         * 此方法用于返回当前Binder对象
         * @return
         */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * 运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理
         */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.binderstudydemo.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.binderstudydemo.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.binderstudydemo.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.binderstudydemo.IBookManager {
            private android.os.IBinder mRemote;

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

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

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

            @Override
            public java.util.List<com.binderstudydemo.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.binderstudydemo.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.binderstudydemo.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.binderstudydemo.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain(); // 3
                android.os.Parcel _reply = android.os.Parcel.obtain(); // 4
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    // 注释5
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.binderstudydemo.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.binderstudydemo.Book book) throws android.os.RemoteException;
}

The code is a bit long, but it is automatically generated by the system. We have to post the code to analyze the processing flow.

First of all, let's analyze this class, IBookManager.Java this class, it inherits the  IInterface  interface, and at the same time it is also an interface itself, all interfaces that can be transmitted in the Binder need to inherit the IInterface  interface.

Now we come back to the three questions raised above. For question 1 :

After the client connects to the server (bindService), there is a code like this in the callback method onServiceConnected:

iAidl = IBookManager.Stub.asInterface(service);

In the comment 2 of the asInterface method of Stub , the queryLocalInterface(DESCRIPTOR) method of Binder is called. We can follow up the code. The code of the queryLocalInterface method of Binder is as follows:

 /**
   * Use information supplied to attachInterface() to return the
   * associated IInterface if it matches the requested
   * descriptor.
   */
public IInterface queryLocalInterface(String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

// 而成员变量mDescriptor的赋值是在Binder的attachInterface方法中,代码如下

/**
  * Convenience method for associating a specific interface with the Binder.
  * After calling, queryLocalInterface() will be implemented for you
  * to return the given owner IInterface when the corresponding
  * descriptor is requested.
  */
public void attachInterface(IInterface owner, String descriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}

Note: The attachInterface method is called in the annotation 1 of the Stub's construction method , which means that DESCRIPTOR is assigned in the Stub's construction method. The client's call does not initialize the Stub, so it is empty; and the IBinder object returned by the onBind method overwritten by the Service on the server side is initialized with a new AIDL.Stub(); so the server side The DESCRIPTOR in is not empty, so when the client calls the Stub.asInterface method, the return value of queryLocalInterface (DESCRIPTOR) is empty, so the method returns Proxy. The purpose of calling the queryLocalInterface method is that when Activity and Service are in the same process, we no longer need to pass information through Binder. For non-same processes, we get a reference to the Proxy object. At this time, we can call the Proxy method. The Proxy implements the methods getBookList and addBook in our custom AIDL. At this point, the write operation can be performed through the Proxy. The _data package is used to store the data sent to the server, and the _reply package is used to store the data returned by the server. This concludes the explanation of question 1.

Now we continue to analyze problem 2 :

On the client side, we call the addBook method through an instance object of AIDL type IBookManager (here we only take one method as an example). The code has reached the last line of IBookManager. Let's continue to look at the place to rewrite this method. At this time, the code has reached the addBook method of Proxy. The writing work of the _data package is done here. In Note 5, Binder's transact method is called. The code is as follows:

public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);

        if (data != null) {
            data.setDataPosition(0);
        }
        // 调用了onTransact方法,
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

In this method, the onTransact method is called, and the onTransact method is overridden in the Stub class of IBookManager. There are also calls to two methods in our custom AIDL in the onTransact method. So far, question 2 has also been clarified.

Note : mRemote.transact(Stub.TRANSACTION_addBook,_data,_reply,0)in the Proxy; after calling this method to enter the Binder, the client thread will be suspended until the server has data returned, the client thread will continue to run .

The meaning of parameter one of this function: In order for the client to tell the server which method is called, it is an int value (assigned at the end of the Proxy), and the server can know the int after AIDL The method corresponding to the value.

Parameter 3: flags, 0 means sending and returning; 1 means not returning.

Now look at the last question 3 :

It can be seen from the above that Binder's onTransact calls the Stub's onTransact method, and the server receives the data in this method and processes it. Determine which method is called by the parameter code. Following the above process, the analysis calls the this.addBook(....) method, but the internal class Stub of IBookManager does not implement addBook, so the addBook method here is the addBook method in the real service. This concludes the analysis of question 3. At this point, we have finished the analysis of the entire call from the client to the server and the processing of the server.

As for the process of calling IBookManager.addBook in Activity, binding Service, and the process of Service creating and returning IBinder, I did not give the code, this is a very common use. The purpose of this article is to analyze the process of AIDL.

Finally, the timing diagram of AIDL is attached:

Regarding the basic learning of Binder, I have written an article before. This article adds a detailed analysis of the AIDL call process on the original basis. The picture of Linux memory space division that appeared at the beginning of the article I used someone else to steal. Hope not to blame.

Here I would like to thank the teacher Leo of Xiangxue Class for explaining the AIDL call process in detail. Thanks again.

Guess you like

Origin blog.csdn.net/zhourui_1021/article/details/84319752