The article directories

  • Summary of binder
  • The code field
    • A complete instance
      • Java instance
      • Native instance
  • Communication between different apps
    • Real-name binder
    • contentprovider
  • conclusion

Let’s start with the origin of Binder and talk about the standard and non-standard writing of Binder for Android. According to the title of the article, the organization form of binder transmission content is described through actual code. Related articles: Android Interprocess Communication Binder – Debug Transaction Android interprocess communication binder – Debug transaction Aidl Binder – layer protocol IPCThreadState Binder – Tool Parcel for Android interprocess Communication

Summary of binder

Openbinder is the ancestor of Binder, which enables interprocess communication and was later used by Google in Android. Binder drivers are located in the drivers/staging/ Android directory at Kernelv3.3. Binder became a full member of the kernel at later versions. The driver code is in the drivers/android/ directory.

Let’s start with a philosophical question: Why does Android use Binder as its primary interprocess communication mode?



Binder is used for interprocess communication, so let’s look at an example:

The code field

The first step is definitely to download and open the Android Studio IDE…

A complete instance

Binder communication was implemented with Java and c++ respectively

Java instance

Create an Empty project and create an AIDL file. Overrides basic transfers (including arrays), custom Object transfers, Binder object transfers (anonymous binder here), file FD transfers; Aidl file keywords: Parcelable, in, out, inout, oneway

// StudentScore.aidl
package com.exp.binder.model;

parcelable StudentScore;
Copy the code
// IBinderTransaction.aidl
package com.exp.binder;

import com.exp.binder.model.StudentScore;

interface IBinderTransaction {
    int transactBasicType(int avgScore, in long[] courses);
    int transactBasicTypeKeywords(in int math, in int english, in int chinese, out int[] avgScore);
    int transactGetObject(out StudentScore stu);
    int transactObject(in StudentScore stu);
    int transactBinder(IBinder binder);
    int transactFd(in ParcelFileDescriptor fd);
    oneway void asyncWork(a);
}
Copy the code
// IBinderToBinder.aidl
package com.exp.binder;

interface IBinderToBinder {
    void testbinder(a);
}
Copy the code

Custom transmission of Objects

package com.exp.binder.model;
import android.os.Parcel;
import android.os.Parcelable;
public class StudentScore implements Parcelable {
    int chinese;
    int math;
    int english;
// Omit the get/set method here.public StudentScore(a){}
    public StudentScore(Parcel in){
        readFromParcel(in);
    }
    @Override
    public int describeContents(a) {
        return 0;
    }
    public void readFromParcel(Parcel in) {
        chinese = in.readInt();
        math = in.readInt();
        english = in.readInt();
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //flags PARCELABLE_WRITE_RETURN_VALUE set when the server returns
        // PARCELABLE_ELIDE_DUPLICATES
        dest.writeInt(chinese);
        dest.writeInt(math);
        dest.writeInt(english);
    }
    public static final Creator<StudentScore> CREATOR = new Creator<StudentScore>() {
        public StudentScore createFromParcel(Parcel source) {
            return new StudentScore(source);
        }
        public StudentScore[] newArray(int size) {
            return newStudentScore[size]; }}; }Copy the code

The key point here is that custom objects are transmitted with binder. You need to define an AIDL file with the same package name.

The service code:

package com.exp.binder.service;
// omit the import class file.public class BinderTransactService extends Service {
    private final static String TAG = "BinderTransactService";
    public BinderTransactService(a) {}@Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return iBinder;
    }
    IBinder iBinder = new IBinderTransaction.Stub() {
        @Override
        public int transactBasicType(int avgScore, long[] courses) throws RemoteException {
            // Simple transfers are not processed
            return 0;
        }
        @Override
        public int transactBasicTypeKeywords(int math, int english, int chinese, int[] avgScore) throws RemoteException {
            avgScore[0] = (math+english+chinese) / 3;
            return avgScore[0];
        }
        @Override
        public int transactGetObject(StudentScore stu) throws RemoteException {
            stu.setChinese(10);
            stu.setEnglish(20);
            stu.setMath(30);
            return 0;
        }
        @Override
        public int transactObject(StudentScore stu) throws RemoteException {
            // Simple transfers are not processed
            return 0;
        }
        @Override
        public int transactBinder(IBinder binder) throws RemoteException {
            IBinderToBinder service = IBinderToBinder.Stub.asInterface(binder);
            service.testbinder();
            return 0;
        }
        @Override
        public int transactFd(ParcelFileDescriptor fd) throws RemoteException {
            int filefd = fd.getFd();
            Path transactFilePath = null;
            Path path = Paths.get("/proc/self/fd/"+filefd);
            try {
                transactFilePath = path.toRealPath();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(transactFilePath ! =null)
                Log.d(TAG, "transactFd: "+transactFilePath.toString());
            return 0;
        }
        @Override
        public void asyncWork(a) throws RemoteException {
            // Since it is an asynchronous call, the client executes the call and immediately returns, even though it sleeps for so long that it does not block the caller
            try {
                Thread.sleep(1000000);
            } catch(InterruptedException e) { e.printStackTrace(); }}}; }Copy the code

Make an asynchronous call: the first run encountered anR, the asynchronous call should not block the main thread, so what happened? (View trace files generated in /data/anr/)

"main" prio=5 tid=1 Sleeping
  | group="main" sCount=1 dsCount=0 obj=0x74a6f000 self=0xa568b400
  | sysTid=5595 nice=0 cgrp=default sched=0/0 handle=0xa99bd534
  | state=S schedstat=( 0 0 0 ) utm=8 stm=4 core=2 HZ=100
  | stack=0xbf3bb000-0xbf3bd000 stackSize=8MB
  | held mutexes=[1K
  at java.lang.Thread.sleep!(Native method)
  - sleeping on <0x0c1185ac> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:371)
  - locked <0x0c1185ac> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:313)
  at com.exp.binder.service.BinderTransactService$1.asyncWork(BinderTransactService.java:84)
  at com.exp.binder.MainActivity.onBtnClick(MainActivity.java:67)
  at com.exp.binder.MainActivity_ViewBinding$1.doClick(MainActivity_ViewBinding.java:33)
  at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:26)
  at android.view.View.performClick(View.java:5610)
  at android.view.View$PerformClick.run(View.java:22265)
  at android.os.Handler.handleCallback(Handler.java:751)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:154)
  at android.app.ActivityThread.main(ActivityThread.java:6077) at java.lang.reflect.Method.invoke! (Native method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Copy the code

Strange how asynchronous calls are executed in the main thread; Two concepts emerge here, local and remote binders. BinderTransactService does not run in a separate process. Binder calls are essentially direct object calls. From your onServiceConnected function, call asInterface. If it’s the same process, return the object directly, otherwise return proxy.

public static com.exp.binder.IBinderTransaction asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if(((iin ! =null) && (iin instanceof com.exp.binder.IBinderTransaction))) {
        return ((com.exp.binder.IBinderTransaction) iin);
    }
    return new com.exp.binder.IBinderTransaction.Stub.Proxy(obj);
}
Copy the code

Add process to the service in Androidmanifest.xml, Android :process=”:binder”; The packagemanagerService checks this property during apK installation, sets the process flag, restarts a process at startService, and loads the Service. Next we see aidl to generate Java files: mRemote, transact (Stub. TRANSACTION_transactBasicTypeKeywords _data while forming, _reply, 0). This function is called in proxy and writes parameters to data, and the last flag 0 indicates synchronous transmission. The client calling end block is on this function. When the server end returns the result, the function will return and write the result in reply. An asynchronous call to reply passes null;

This file is automatically generated by AIDL, this is the standard way, the above said in out keywords in this file generated code logic is not the same, the next article aiDL we will look at in detail/* * This file is auto-generated. DO NOT MODIFY. */
package com.exp.binder;

public interface IBinderTransaction extends android.os.IInterface {
    /** * Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.exp.binder.IBinderTransaction {
        private static final java.lang.String DESCRIPTOR = "com.exp.binder.IBinderTransaction";

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

        /** * Cast an IBinder object into an com.exp.binder.IBinderTransaction interface, * generating a proxy if needed. */
        public static com.exp.binder.IBinderTransaction asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if(((iin ! =null) && (iin instanceof com.exp.binder.IBinderTransaction))) {
                return ((com.exp.binder.IBinderTransaction) iin);
            }
            return new com.exp.binder.IBinderTransaction.Stub.Proxy(obj);
        }

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

        @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_transactBasicType: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    long[] _arg1;
                    _arg1 = data.createLongArray();
                    int _result = this.transactBasicType(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_transactBasicTypeKeywords: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _arg2;
                    _arg2 = data.readInt();
                    int[] _arg3;
                    int _arg3_length = data.readInt();
                    if ((_arg3_length < 0)) {
                        _arg3 = null;
                    } else {
                        _arg3 = new int[_arg3_length];
                    }
                    int _result = this.transactBasicTypeKeywords(_arg0, _arg1, _arg2, _arg3);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    reply.writeIntArray(_arg3);
                    return true; }...case TRANSACTION_asyncWork: {
                    data.enforceInterface(descriptor);
                    this.asyncWork();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags); }}}private static class Proxy implements com.exp.binder.IBinderTransaction {
            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) {
                returnDESCRIPTOR; }...@Override
            public int transactBasicTypeKeywords(int math, int english, int chinese, int[] avgScore) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(math);
                    _data.writeInt(english);
                    _data.writeInt(chinese);
                    if ((avgScore == null)) {
                        _data.writeInt(-1);
                    } else {
                        _data.writeInt(avgScore.length);
                    }
                    mRemote.transact(Stub.TRANSACTION_transactBasicTypeKeywords, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                    _reply.readIntArray(avgScore);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return_result; }...@Override
            public void asyncWork(a) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_asyncWork, _data, null, android.os.IBinder.FLAG_ONEWAY);
                } finally{ _data.recycle(); }}}static final int TRANSACTION_transactBasicType = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_transactBasicTypeKeywords = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_transactGetObject = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_transactObject = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_transactBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
        static final int TRANSACTION_transactFd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
        static final int TRANSACTION_asyncWork = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
    }

    public int transactBasicType(int avgScore, long[] courses) throws android.os.RemoteException;

    public int transactBasicTypeKeywords(int math, int english, int chinese, int[] avgScore) throws android.os.RemoteException;

    public int transactGetObject(com.exp.binder.model.StudentScore stu) throws android.os.RemoteException;

    public int transactObject(com.exp.binder.model.StudentScore stu) throws android.os.RemoteException;

    public int transactBinder(android.os.IBinder binder) throws android.os.RemoteException;

    public int transactFd(android.os.ParcelFileDescriptor fd) throws android.os.RemoteException;

    public void asyncWork(a) throws android.os.RemoteException;
}
Copy the code

Native instance

Android source code url :aospxref.com/, the following code version is and… Native example we look at a media service source: aospxref.com/android-11….

Public BinderService; public BnMediaLogService; public BinderService; 2.BinderService implements instantiate->push(), and adds this MediaLogService to defaultServiceManager to overwrite getServiceName Services. 3. After the initialization, joinThreadPool polls binder Servers.

There are several macro definitions in BinderService

IMPLEMENT_META_INTERFACE(MediaLogService, "android.media.IMediaLogService");
Copy the code

Implement asInterface and other binder must interface.

Communication between different apps

At the end, I wonder if you have such a question. All the examples above are completed in the same APP project. How can different apps communicate with each other?

Real-name binder

Android provides a service command that finds the real name Binder from the Servicemanager, returns the IBinder object, and calls the remote process using the Transact method. (Leave the logic for the next article…)

Adb shell service list # This command displays the added ADB shell service Call activity code provided by binder I32 val1 s16 string1 # Write in the order in which the service onTransact method reads

contentprovider

Write a provider and register it in system_server. Other apps find the corresponding provider from the server and perform the corresponding add, delete, change and check interface, which will call the service process. Upload sample to Github later, and post the link here.

conclusion

Read through, as if there is no answer to the questions reserved above; Binder can’t be covered in one document; Give the answer first; Several other interprocess communication methods can implement a simple communication sample in user space with a few lines of code; Unable to do safety and effectiveness verification; Therefore, Android uses binder, kernel driver is just a transaction implementation, binder protocol is done in the upper user space verification, libbinder.so, namely IPCThreadState implementation.

See here, hard to give a thumbs up! \