A list,

This will be a series of articles around AIDL, including simple use of AIDL, advanced use of AIDL, AIDL source exploration, hope to start from simple to complex, in this process to enable you to master the use of AIDL and the details need to pay attention to, but also through the exploration of AIDL source, so that you can understand the principle of AIDL processing. This is the third article, based on the first two articles, according to the AIDL interface in the example, in-depth source code to explore the implementation principle of AIDL.

  • AIDL for Android IPC
  • AIDL for Android IPC
  • Android IPC AIDL

Article vocabulary explanation:

  • AS: AndroidStudio development tool
  • AIDL file: indicates the AIDL interface created in the AIDL directory
  • AIDL class: refers to the class generated by the AIDL file Build

AIDL file structure

First, review the AIDL files defined in the previous two articles.

interface MyAIDLInterface {
    /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
    void commonMethod(a);

    void setStringText(String text);

    void setObjectMethodIn(in MethodObject o);
    void setObjectMethodOut(out MethodObject o);
    void setObjectMethodInout(inout MethodObject o);

    MethodObject getObjectMethod(a);

    void register(CallBackAIDLInterface aidl);
    void unregister(CallBackAIDLInterface aidl);
}
Copy the code

To get an overview of AIDL’s handling logic for different types of functions, there are eight functions defined here, register() and unregister() are the same, so there are seven different types in total. Now go to the AIDL class, the real Hand behind the Curtain, and it’s a little long, and you might be sick of it, but trust me you’re going to have to be patient with it.

public interface MyAIDLInterface extends android.os.IInterface
{
  /** Default implementation for MyAIDLInterface. */
  public static class Default implements com.zhukai.aidlservice.MyAIDLInterface
  {
    /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
    @Override public void commonMethod(a) throws android.os.RemoteException
    {}@Override public void setStringText(java.lang.String text) throws android.os.RemoteException
    {}@Override public void setObjectMethodIn(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
    {}@Override public void setObjectMethodOut(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
    {}@Override public void setObjectMethodInout(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
    {}@Override public com.zhukai.aidlservice.MethodObject getObjectMethod(a) throws android.os.RemoteException
    {
      return null;
    }
    @Override public void register(com.zhukai.aidlservice.CallBackAIDLInterface aidl) throws android.os.RemoteException
    {}@Override public void unregister(com.zhukai.aidlservice.CallBackAIDLInterface aidl) throws android.os.RemoteException
    {}@Override
    public android.os.IBinder asBinder(a) {
      return null; }}/** Local-side IPC implementation stub class. */
  public static abstract class Stub extends android.os.Binder implements com.zhukai.aidlservice.MyAIDLInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.zhukai.aidlservice.MyAIDLInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub(a)
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /** * Cast an IBinder object into an com.zhukai.aidlservice.MyAIDLInterface interface, * generating a proxy if needed. */
    public static com.zhukai.aidlservice.MyAIDLInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if(((iin! =null)&&(iin instanceof com.zhukai.aidlservice.MyAIDLInterface))) {
        return ((com.zhukai.aidlservice.MyAIDLInterface)iin);
      }
      return new com.zhukai.aidlservice.MyAIDLInterface.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_commonMethod:
        {
          data.enforceInterface(descriptor);
          this.commonMethod();
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_setStringText:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.setStringText(_arg0);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_setObjectMethodIn:
        {
          data.enforceInterface(descriptor);
          com.zhukai.aidlservice.MethodObject _arg0;
          if ((0! =data.readInt())) { _arg0 = com.zhukai.aidlservice.MethodObject.CREATOR.createFromParcel(data); }else {
            _arg0 = null;
          }
          this.setObjectMethodIn(_arg0);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_setObjectMethodOut:
        {
          data.enforceInterface(descriptor);
          com.zhukai.aidlservice.MethodObject _arg0;
          _arg0 = new com.zhukai.aidlservice.MethodObject();
          this.setObjectMethodOut(_arg0);
          reply.writeNoException();
          if((_arg0! =null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        case TRANSACTION_setObjectMethodInout:
        {
          data.enforceInterface(descriptor);
          com.zhukai.aidlservice.MethodObject _arg0;
          if ((0! =data.readInt())) { _arg0 = com.zhukai.aidlservice.MethodObject.CREATOR.createFromParcel(data); }else {
            _arg0 = null;
          }
          this.setObjectMethodInout(_arg0);
          reply.writeNoException();
          if((_arg0! =null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        case TRANSACTION_getObjectMethod:
        {
          data.enforceInterface(descriptor);
          com.zhukai.aidlservice.MethodObject _result = this.getObjectMethod();
          reply.writeNoException();
          if((_result! =null)) {
            reply.writeInt(1);
            _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          }
          else {
            reply.writeInt(0);
          }
          return true;
        }
        case TRANSACTION_register:
        {
          data.enforceInterface(descriptor);
          com.zhukai.aidlservice.CallBackAIDLInterface _arg0;
          _arg0 = com.zhukai.aidlservice.CallBackAIDLInterface.Stub.asInterface(data.readStrongBinder());
          this.register(_arg0);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_unregister:
        {
          data.enforceInterface(descriptor);
          com.zhukai.aidlservice.CallBackAIDLInterface _arg0;
          _arg0 = com.zhukai.aidlservice.CallBackAIDLInterface.Stub.asInterface(data.readStrongBinder());
          this.unregister(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags); }}}private static class Proxy implements com.zhukai.aidlservice.MyAIDLInterface
    {
      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;
      }
      /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
      @Override public void commonMethod(a) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_commonMethod, _data, _reply, 0);
          if(! _status && getDefaultImpl() ! =null) {
            getDefaultImpl().commonMethod();
            return;
          }
          _reply.readException();
        }
        finally{ _reply.recycle(); _data.recycle(); }}@Override public void setStringText(java.lang.String text) 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(text);
          boolean _status = mRemote.transact(Stub.TRANSACTION_setStringText, _data, _reply, 0);
          if(! _status && getDefaultImpl() ! =null) {
            getDefaultImpl().setStringText(text);
            return;
          }
          _reply.readException();
        }
        finally{ _reply.recycle(); _data.recycle(); }}@Override public void setObjectMethodIn(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if((o! =null)) {
            _data.writeInt(1);
            o.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_setObjectMethodIn, _data, _reply, 0);
          if(! _status && getDefaultImpl() ! =null) {
            getDefaultImpl().setObjectMethodIn(o);
            return;
          }
          _reply.readException();
        }
        finally{ _reply.recycle(); _data.recycle(); }}@Override public void setObjectMethodOut(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_setObjectMethodOut, _data, _reply, 0);
          if(! _status && getDefaultImpl() ! =null) {
            getDefaultImpl().setObjectMethodOut(o);
            return;
          }
          _reply.readException();
          if ((0!=_reply.readInt())) {
            o.readFromParcel(_reply);
          }
        }
        finally{ _reply.recycle(); _data.recycle(); }}@Override public void setObjectMethodInout(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if((o! =null)) {
            _data.writeInt(1);
            o.writeToParcel(_data, 0);
          }
          else {
            _data.writeInt(0);
          }
          boolean _status = mRemote.transact(Stub.TRANSACTION_setObjectMethodInout, _data, _reply, 0);
          if(! _status && getDefaultImpl() ! =null) {
            getDefaultImpl().setObjectMethodInout(o);
            return;
          }
          _reply.readException();
          if ((0!=_reply.readInt())) {
            o.readFromParcel(_reply);
          }
        }
        finally{ _reply.recycle(); _data.recycle(); }}@Override public com.zhukai.aidlservice.MethodObject getObjectMethod(a) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        com.zhukai.aidlservice.MethodObject _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getObjectMethod, _data, _reply, 0);
          if(! _status && getDefaultImpl() ! =null) {
            return getDefaultImpl().getObjectMethod();
          }
          _reply.readException();
          if ((0! =_reply.readInt())) { _result = com.zhukai.aidlservice.MethodObject.CREATOR.createFromParcel(_reply); }else {
            _result = null; }}finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      @Override public void register(com.zhukai.aidlservice.CallBackAIDLInterface aidl) 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.writeStrongBinder((((aidl! =null))? (aidl.asBinder()):(null)));
          boolean _status = mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0);
          if(! _status && getDefaultImpl() ! =null) {
            getDefaultImpl().register(aidl);
            return;
          }
          _reply.readException();
        }
        finally{ _reply.recycle(); _data.recycle(); }}@Override public void unregister(com.zhukai.aidlservice.CallBackAIDLInterface aidl) 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.writeStrongBinder((((aidl! =null))? (aidl.asBinder()):(null)));
          boolean _status = mRemote.transact(Stub.TRANSACTION_unregister, _data, _reply, 0);
          if(! _status && getDefaultImpl() ! =null) {
            getDefaultImpl().unregister(aidl);
            return;
          }
          _reply.readException();
        }
        finally{ _reply.recycle(); _data.recycle(); }}public static com.zhukai.aidlservice.MyAIDLInterface sDefaultImpl;
    }
    static final int TRANSACTION_commonMethod = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_setStringText = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_setObjectMethodIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    static final int TRANSACTION_setObjectMethodOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    static final int TRANSACTION_setObjectMethodInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
    static final int TRANSACTION_getObjectMethod = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
    static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
    static final int TRANSACTION_unregister = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
    public static boolean setDefaultImpl(com.zhukai.aidlservice.MyAIDLInterface impl) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      if(Stub.Proxy.sDefaultImpl ! =null) {
        throw new IllegalStateException("setDefaultImpl() called twice");
      }
      if(impl ! =null) {
        Stub.Proxy.sDefaultImpl = impl;
        return true;
      }
      return false;
    }
    public static com.zhukai.aidlservice.MyAIDLInterface getDefaultImpl(a) {
      returnStub.Proxy.sDefaultImpl; }}/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */
  public void commonMethod(a) throws android.os.RemoteException;
  public void setStringText(java.lang.String text) throws android.os.RemoteException;
  public void setObjectMethodIn(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException;
  public void setObjectMethodOut(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException;
  public void setObjectMethodInout(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException;
  public com.zhukai.aidlservice.MethodObject getObjectMethod(a) throws android.os.RemoteException;
  public void register(com.zhukai.aidlservice.CallBackAIDLInterface aidl) throws android.os.RemoteException;
  public void unregister(com.zhukai.aidlservice.CallBackAIDLInterface aidl) throws android.os.RemoteException;
}
Copy the code

This class is too long, but I believe you still have a little impression of this class. Now we just need to sort out our ideas, first make clear the structure of the class, and then use it as an entrance to track, everything will be clear! I’m going to simplify the code a little bit so you can see the structure of the class.

public interface MyAIDLInterface extends android.os.IInterface
{
  // Default implementation class
  public static class Default implements com.zhukai.aidlservice.MyAIDLInterface
  {...// Omit the inherited function
  }
  // Server side Binder object
  public static abstract class Stub extends android.os.Binder implements com.zhukai.aidlservice.MyAIDLInterface
  {...// omit the content
    // Client proxy object
    private static class Proxy implements com.zhukai.aidlservice.MyAIDLInterface
    {}...// omit the content
  }
  // all functions defined in the AIDL file
  public void commonMethod(a) throws android.os.RemoteException;
  public void setStringText(java.lang.String text) throws android.os.RemoteException;
  public void setObjectMethodIn(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException;
  public void setObjectMethodOut(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException;
  public void setObjectMethodInout(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException;
  public com.zhukai.aidlservice.MethodObject getObjectMethod(a) throws android.os.RemoteException;
  public void register(com.zhukai.aidlservice.CallBackAIDLInterface aidl) throws android.os.RemoteException;
  public void unregister(com.zhukai.aidlservice.CallBackAIDLInterface aidl) throws android.os.RemoteException;
}
Copy the code

Now the class structure is much simpler. AS builds the outermost MyAIDLInterface class from the AIDL file we defined, which contains all the functions defined in the AIDL file. In addition, three internal implementation classes Default, Stub, Proxy (Stub internal class) are constructed. Default is a Default implementation class. Stub and Proxy are two of the most important AIDL classes. Stub is the server, and therefore it also inherits Binder (Binder implements the IBinde interface), and Proxy is the client. With the structure figured out, you can now go back to use and trace the internal implementation logic through the process used.

Third, source tracking

1. Create Binder objects on the server

Remember that in the server Service, the onBind method returns a Stub object.

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

MyAIDLInterface.Stub stub = newMyAIDLInterface.Stub() {... }Copy the code

Here’s what you need to look at when the Stub object is created.

private static final java.lang.String DESCRIPTOR = "com.zhukai.aidlservice.MyAIDLInterface";
/** Construct the stub at attach it to the interface. */

private IInterface mOwner;
private String mDescriptor;

public Stub(a)
{
  this.attachInterface(this, DESCRIPTOR);// call Binder's function
}

public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
    mOwner = owner;/ / Stub object
    mDescriptor = descriptor;/ / descriptors
}
Copy the code

Bind yourself to a descriptor with the Binder object by calling attachInterface() on the Binder parent. The Binder object returned in onBind() carries the Stub object and a descriptor that is its own complete class name.

Where does the Binder object end up being returned? If it is the place to bind Service, then go to the client side.

2. The client obtains the operation object

private void bind(a) {
    Intent intent = new Intent();
    intent.setAction("com.zhukai.aidlservice.startService");
    intent.setComponent(new ComponentName("com.zhukai.aidlservice"."com.zhukai.aidlservice.MyService"));
    bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myAIDLInterface = MyAIDLInterface.Stub.asInterface(service);// Core method. }@Override
        public void onServiceDisconnected(ComponentName name) {
            myAIDLInterface = null;
        }
    }, Service.BIND_AUTO_CREATE);
}
Copy the code

In the client binding method, the onServiceConnected callback method of the ServiceConnection interface returns an IBinder object, which is the Binder object just returned from the Service. Note that the Binder object is the same as the server if the service is not being processed. The Binder object is just a BinderProxy object that the server returns with the Binder object. Now that Binder has passed from the server to the client, see what the client does next.

Then call MyAIDLInterface. Stub. AsInterface (service) functions to Binder object, it is a special for the client action object function.


// The descriptor is the full class name of the AIDL class
private static final java.lang.String DESCRIPTOR = "com.zhukai.aidlservice.MyAIDLInterface";

/** * Cast an IBinder object into an com.zhukai.aidlservice.MyAIDLInterface interface, * generating a proxy if needed. */
public static com.zhukai.aidlservice.MyAIDLInterface asInterface(android.os.IBinder obj)
{
  if ((obj==null)) {/ / found empty
    return null;
  }
  android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);/ / core
  if(((iin! =null)&&(iin instanceof com.zhukai.aidlservice.MyAIDLInterface))) {
    return ((com.zhukai.aidlservice.MyAIDLInterface)iin);
  }
  return new com.zhukai.aidlservice.MyAIDLInterface.Stub.Proxy(obj);
}
Copy the code

In the function, Binder is null, and then the queryLocalInterface() function of the Binder object is called, passing in the client DESCRIPTOR.

public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
    if(mDescriptor ! =null && mDescriptor.equals(descriptor)) {// Compare with the Binder cache descriptor
        return mOwner;
    }
    return null;
}
Copy the code

If the Binder object is the same as the server object, the server object is bound to a mDescriptor and a Stub object (mOwner), which is equal to the client descriptor that was passed in. That will return the bound server Stub object. If the Binder pair is like a proxy object, the mDescriptor must be NULL, so return NULL.

Execute the exit method and return to the asInterface() function.

public static com.zhukai.aidlservice.MyAIDLInterface asInterface(android.os.IBinder obj)
{
  if ((obj==null)) {
    return null;
  }
  android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  if(((iin! =null)&&(iin instanceof com.zhukai.aidlservice.MyAIDLInterface))) {// Is not null and the type matches
    return ((com.zhukai.aidlservice.MyAIDLInterface)iin);// Convert the Stub object to the implemented AIDL class type
  }
  Binder proxy object is passed in.
  return new com.zhukai.aidlservice.MyAIDLInterface.Stub.Proxy(obj);
}
Copy the code

If the IIN object is a server-side Stub object, it is directly converted to the AIDL class type and returned as null. Binder Proxy objects are passed in to create Proxy object returns. Anyway, the client ends up with an AIDL-class object that Binder objects return differently. This article focuses on a separate process around a Service, with Binder as a Proxy object, and this method executes to the last step, returning a Proxy object. Because the server Service is in the same process, the client gets the Stub object of the server, so there is no IPC, and there is no need for further analysis.

3. Internal implementation analysis of various types of functions

When the client gets the Proxy object, it can call all functions to communicate with the server.

if (null! = myAIDLInterface){ myAIDLInterface.commonMethod(); myAIDLInterface.setStringText("test");
    myAIDLInterface.setObjectMethodIn(new MethodObject());
    myAIDLInterface.setObjectMethodInout(new MethodObject());
    myAIDLInterface.setObjectMethodInout(new MethodObject());
    myAIDLInterface.getObjectMethod();
    myAIDLInterface.register(callBackAIDLInterface);
    myAIDLInterface.unregister(callBackAIDLInterface);
}

CallBackAIDLInterface callBackAIDLInterface = new CallBackAIDLInterface.Stub() {
    @Override
    public void callBack(a) throws RemoteException {}};Copy the code

commonMethod()

MyAIDLInterface is a Proxy object, so MyAIDLInterface is a Proxy object.

@Override public void commonMethod(a) throws android.os.RemoteException
    {
  // Serialize the operation object Parcel is ready
  android.os.Parcel _data = android.os.Parcel.obtain();
  android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
    _data.writeInterfaceToken(DESCRIPTOR);// Write the descriptor
    boolean _status = mRemote.transact(Stub.TRANSACTION_commonMethod, _data, _reply, 0);// Bind calls the server onTransact() function, and the client enters the blocking state.
    if(! _status && getDefaultImpl() ! =null) {// Return false if the Binder failed to communicate, or if it succeeded.
      getDefaultImpl().commonMethod();// Invoke the default implementation
      return;
    }
    _reply.readException();// Read whether the server execution is abnormal
  }
  finally {// Release the serialized action object_reply.recycle(); _data.recycle(); }}Copy the code

Since it is an IPC process, serialization is required for transferring data, so two serialization operation objects (_data, _reply) are created. It then writes a descriptor and calls mRemote’s transact() function directly, passing in an identifier (stub.transaction_commonMethod) and two serialized objects (_data, _reply) and an int (flags). This identifier is what distinguishes a function. Call it a function identifier.

static final int TRANSACTION_commonMethod = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setStringText = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setObjectMethodIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_setObjectMethodOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_setObjectMethodInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_getObjectMethod = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
static final int TRANSACTION_unregister = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
Copy the code

MRemote is the Binder object passed in when the Proxy object is initialized; I forgot to recall the previous analysis of the asInterface() function. Mremote.transact () is blocked, The Binder mechanism notifies the server of the onTransact() function in the Stub object (mremote.transact ()-> server binder.ontransact ()-> overridden onTransact() in the Stub class), Now let’s go to the onTransact() function in the Stub class and see what it does.

@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_commonMethod:
    {
      data.enforceInterface(descriptor);
      this.commonMethod();
      reply.writeNoException();
      return true;
    }
    case TRANSACTION_setStringText:
    {
      data.enforceInterface(descriptor);
      java.lang.String _arg0;
      _arg0 = data.readString();
      this.setStringText(_arg0);
      reply.writeNoException();
      return true;
    }
    case TRANSACTION_setObjectMethodIn:
    {
      data.enforceInterface(descriptor);
      com.zhukai.aidlservice.MethodObject _arg0;
      if ((0! =data.readInt())) { _arg0 = com.zhukai.aidlservice.MethodObject.CREATOR.createFromParcel(data); }else {
        _arg0 = null;
      }
      this.setObjectMethodIn(_arg0);
      reply.writeNoException();
      return true;
    }
    case TRANSACTION_setObjectMethodOut:
    {
      data.enforceInterface(descriptor);
      com.zhukai.aidlservice.MethodObject _arg0;
      _arg0 = new com.zhukai.aidlservice.MethodObject();
      this.setObjectMethodOut(_arg0);
      reply.writeNoException();
      if((_arg0! =null)) {
        reply.writeInt(1);
        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
      }
      else {
        reply.writeInt(0);
      }
      return true;
    }
    case TRANSACTION_setObjectMethodInout:
    {
      data.enforceInterface(descriptor);
      com.zhukai.aidlservice.MethodObject _arg0;
      if ((0! =data.readInt())) { _arg0 = com.zhukai.aidlservice.MethodObject.CREATOR.createFromParcel(data); }else {
        _arg0 = null;
      }
      this.setObjectMethodInout(_arg0);
      reply.writeNoException();
      if((_arg0! =null)) {
        reply.writeInt(1);
        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
      }
      else {
        reply.writeInt(0);
      }
      return true;
    }
    case TRANSACTION_getObjectMethod:
    {
      data.enforceInterface(descriptor);
      com.zhukai.aidlservice.MethodObject _result = this.getObjectMethod();
      reply.writeNoException();
      if((_result! =null)) {
        reply.writeInt(1);
        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
      }
      else {
        reply.writeInt(0);
      }
      return true;
    }
    case TRANSACTION_register:
    {
      data.enforceInterface(descriptor);
      com.zhukai.aidlservice.CallBackAIDLInterface _arg0;
      _arg0 = com.zhukai.aidlservice.CallBackAIDLInterface.Stub.asInterface(data.readStrongBinder());
      this.register(_arg0);
      reply.writeNoException();
      return true;
    }
    case TRANSACTION_unregister:
    {
      data.enforceInterface(descriptor);
      com.zhukai.aidlservice.CallBackAIDLInterface _arg0;
      _arg0 = com.zhukai.aidlservice.CallBackAIDLInterface.Stub.asInterface(data.readStrongBinder());
      this.unregister(_arg0);
      reply.writeNoException();
      return true;
    }
    default:
    {
      return super.onTransact(code, data, reply, flags); }}}Copy the code

As a whole, different logical processing is done through different function identifiers. The commonMethod() function is a TRANSACTION_commonMethod branch.

case TRANSACTION_commonMethod:
{
  data.enforceInterface(descriptor);// Validates the descriptor
  this.commonMethod();// Call its own commonMethod() function, which is the server-side Stub object's commonMethod() function.
  reply.writeNoException();// The write is normal
  return true;
}
Copy the code

The descriptor is first validated and this.monMethod () is called. This is the Stub object that calls the server Stub object’s commonMethod() function. If no exception is displayed, the execution succeeds. After the server completes, the client block recovery run continues with the following code.

if(! _status && getDefaultImpl() ! =null) {
  getDefaultImpl().commonMethod();
  return;
}
_reply.readException();
Copy the code

Failure is generally not set the default implementation, so the subsequent execution is to read the server side write exceptions, for exception detection, if the server side write exceptions, the client will throw exceptions. Interested can click into the source code to see. Thus a complete communication is completed.

Note: after analyzing commonMethod() function, we have made clear the basic communication logic of AIDL. The following different types of function implementation are only different in the processing details, which are in the client-side Proxy implementation function and the server-side Stub class onTransact() function. Therefore, we can only analyze these two functions, and directly put the two functions of the customer side and the server side together, which is easy to see.

setStringText(String text)

// Client Proxy class
@Override public void setStringText(java.lang.String text) throws android.os.RemoteException
{
  // Serialize the operation object Parcel is ready
  android.os.Parcel _data = android.os.Parcel.obtain();
  android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
    _data.writeInterfaceToken(DESCRIPTOR);// Write the descriptor
    _data.writeString(text);// Write the incoming data
    boolean _status = mRemote.transact(Stub.TRANSACTION_setStringText, _data, _reply, 0);// Bind calls the server onTransact() function, and the client enters the blocking state.
    if(! _status && getDefaultImpl() ! =null) {// Return false if the Binder failed to communicate, or if it succeeded.
      getDefaultImpl().setStringText(text);// Invoke the default implementation
      return;
    }
    _reply.readException();// Read whether the server execution is abnormal
  }
  finally {// Release the serialized action object_reply.recycle(); _data.recycle(); }}// Server-side Stub class
@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 TRANSACTION_setStringText:
        {
          data.enforceInterface(descriptor);// Validates the descriptor
          java.lang.String _arg0;
          _arg0 = data.readString();// Read the data written by the client
          this.setStringText(_arg0);// Call your own function and pass in the read data
          reply.writeNoException();// The write is normal
          return true; }}}Copy the code

The main difference is that the client has one more step to write data, while the server has one more step to read data.

void setObjectMethodIn(in MethodObject o)

// Client Proxy class
@Override public void setObjectMethodIn(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
{
  // Serialize the operation object Parcel is ready
  android.os.Parcel _data = android.os.Parcel.obtain();
  android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
    _data.writeInterfaceToken(DESCRIPTOR);// Write the descriptor
    if((o! =null)) {// The data object is not null
      _data.writeInt(1);// Write 1 to indicate that the serialized object is not null
      o.writeToParcel(_data, 0);// Write the serialized object
    }
    else {
      _data.writeInt(0);// Write 0 to indicate null serialized data object
    }
    boolean _status = mRemote.transact(Stub.TRANSACTION_setObjectMethodIn, _data, _reply, 0);// Bind calls the server onTransact() function, and the client enters the blocking state.
     if(! _status && getDefaultImpl() ! =null) {// Return false if the Binder failed to communicate, or if it succeeded.
      getDefaultImpl().setObjectMethodIn(o);// Invoke the default implementation
      return;
    }
    _reply.readException();// Read whether the server execution is abnormal
  }
  finally {// Release the serialized action object_reply.recycle(); _data.recycle(); }}// Server-side Stub class
@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 TRANSACTION_setObjectMethodIn:
        {
          data.enforceInterface(descriptor);// Validates the descriptor
          com.zhukai.aidlservice.MethodObject _arg0;
          if ((0! =data.readInt())) {// The client writes either 0 or 1. Non-0 means the serialized object is not null
            _arg0 = com.zhukai.aidlservice.MethodObject.CREATOR.createFromParcel(data);// Deserialize
          }
          else {
            _arg0 = null;
          }
         this.setObjectMethodIn(_arg0);// Call your own function and pass in the data object.
         reply.writeNoException();// The write is normal
         return true; }}}Copy the code

The setObjectMethodIn() function parameter is an object type prefixed with the IN modifier in the AIDL file. The data object of the parameter can only be passed to the server from the client, so it is logically serialized on the client and deserialized on the server, and changes on the server do not affect the client.

setObjectMethodOut(out MethodObject o)

// Client Proxy class
@Override public void setObjectMethodOut(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
{
  // Serialize the operation object Parcel is ready
  android.os.Parcel _data = android.os.Parcel.obtain();
  android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
    _data.writeInterfaceToken(DESCRIPTOR);// Write the descriptor
    boolean _status = mRemote.transact(Stub.TRANSACTION_setObjectMethodOut, _data, _reply, 0);// Bind calls the server onTransact() function, and the client enters the blocking state.
    if(! _status && getDefaultImpl() ! =null) {// Return false if the Binder failed to communicate, or if it succeeded.
      getDefaultImpl().setObjectMethodOut(o);// Invoke the default implementation
      return;
    }
    _reply.readException();// Read whether the server execution is abnormal
    if ((0! =_reply.readInt())) {// The server writes either 0 or 1. Non-0 indicates that the serialized object is not NULL
      o.readFromParcel(_reply);// Deserialize the data object o in the parameter}}finally {// Release the serialized action object_reply.recycle(); _data.recycle(); }}// Server-side Stub class
@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 TRANSACTION_setObjectMethodOut:
        {
          data.enforceInterface(descriptor);// Validates the descriptor
          com.zhukai.aidlservice.MethodObject _arg0;
          _arg0 = new com.zhukai.aidlservice.MethodObject();// Create a data object
          this.setObjectMethodOut(_arg0);// Call your own function, passing in the object you just created.
          reply.writeNoException();// The write is normal
          if((_arg0! =null)) {// The data object is not null
            reply.writeInt(1);// Write 1 to indicate that the serialized data object is not null
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);// Write the serialized object
          }
          else {
            reply.writeInt(0);// Write 0 to indicate null serialized data object
          }
          return true; }}}Copy the code

The setObjectMethodOut() function argument is also an object type and is prefixed with the Out modifier in the AIDL file. The client parameter data object cannot be transferred to the server, so no serialization write is performed until the client calls mremote.transact (); However, because the modification of the server side can affect the client side, a data object is directly created in the processing of the server side, and passed in when calling the function, so that the server side can operate. After the execution of the method, a serialized write operation is carried out. When the server completes the execution without exception, the client stops blocking. Instead, the client modifies the original data object through deserialization.

setObjectMethodInout(inout MethodObject o)

Client Proxy class@Override public void setObjectMethodInout(com.zhukai.aidlservice.MethodObject o) throws android.os.RemoteException
{
  // Serialize the operation class Pracel
  android.os.Parcel _data = android.os.Parcel.obtain();
  android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
    _data.writeInterfaceToken(DESCRIPTOR);// Write the descriptor
    if((o! =null)) {// The data object is not null
      _data.writeInt(1);// Write 1 to indicate that the serialized data object is not null
      o.writeToParcel(_data, 0);// Write the serialized object
    }
    else {
      _data.writeInt(0);// Write 0 to indicate null serialized data object
    }
    boolean _status = mRemote.transact(Stub.TRANSACTION_setObjectMethodInout, _data, _reply, 0);// Bind calls the server onTransact() function, and the client enters the blocking state.
    if(! _status && getDefaultImpl() ! =null) {// Return false if the Binder failed to communicate, or if it succeeded.
      getDefaultImpl().setObjectMethodInout(o);// Invoke the default implementation
      return;
    }
    _reply.readException();// Read whether the server execution is abnormal
    if ((0! =_reply.readInt())) {// The server writes either 0 or 1. Non-0 indicates that the serialized object is not NULL
      o.readFromParcel(_reply);// Deserialize the data object o in the parameter}}finally {// Release the serialized action object_reply.recycle(); _data.recycle(); }}// Server-side Stub class
@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 TRANSACTION_setObjectMethodInout:
        {
          data.enforceInterface(descriptor);// Validates the descriptor
          com.zhukai.aidlservice.MethodObject _arg0;
          if ((0! =data.readInt())) {// The client writes either 0 or 1. Non-0 means the serialized object is not null
             _arg0 = com.zhukai.aidlservice.MethodObject.CREATOR.createFromParcel(data);// Deserialize
          }
          else {
            _arg0 = null;
          }
            this.setObjectMethodInout(_arg0);// Call your own function, passing in the data object.
            reply.writeNoException();// The write is normal
          if((_arg0! =null)) {// The data object is not null
            reply.writeInt(1);// Write 1 to indicate that the serialized data object is not null
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);// Write the serialized object
          }
          else {
            reply.writeInt(0);// Write 0 to indicate null serialized data object
          }
          return true; }}}Copy the code

The setObjectMethodInout() function argument is also an object type, prefixed with the inout modifier in the AIDL file. The data object of the argument can be transmitted to the server, and the modification of the data object can also be synchronized to the client. Therefore, in terms of logical processing, the customer side serializes and deserializes the mremote.transact () function before calling it. The server passes in the deserialized data object when calling its own function, and serializes the data object again after the function is executed. After the server completes execution, the client stops blocking, and the client also performs a deserialization, modifying the data object data passed in by the parameter. It is a logical combination of the previous two functions, a serialization and deserialization operation from the customer side to the server side, and also a serialization and deserialization operation from the server side to the client side.

MethodObject getObjectMethod()

All previous functions have no return value. This function has no return value.

// Client Proxy class
@Override public com.zhukai.aidlservice.MethodObject getObjectMethod(a) throws android.os.RemoteException
{
  // Serialize the operation object Parcel is ready
  android.os.Parcel _data = android.os.Parcel.obtain();
  android.os.Parcel _reply = android.os.Parcel.obtain();
  com.zhukai.aidlservice.MethodObject _result;
  try {
    _data.writeInterfaceToken(DESCRIPTOR);// Write the descriptor
    boolean _status = mRemote.transact(Stub.TRANSACTION_getObjectMethod, _data, _reply, 0);// Bind calls the server onTransact() function, and the client enters the blocking state
    if(! _status && getDefaultImpl() ! =null) {// Return false if the Binder failed to communicate, or if it succeeded.
      return getDefaultImpl().getObjectMethod();// Invoke the default implementation
    }
    _reply.readException();// Read whether the server execution is abnormal
    if ((0! =_reply.readInt())) {// The server writes either 0 or 1. Non-0 indicates that the serialized object is not NULL
      _result = com.zhukai.aidlservice.MethodObject.CREATOR.createFromParcel(_reply);// Deserialize
    }
    else {
      _result = null; }}finally {// Release the serialized action object
    _reply.recycle();
    _data.recycle();
  }
  return _result;// Returns a data object
}
// Server-side Stub class
@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 TRANSACTION_getObjectMethod:
        {
          data.enforceInterface(descriptor);// perform descriptor verification
          com.zhukai.aidlservice.MethodObject _result = this.getObjectMethod();// Call your own function live data object.
          reply.writeNoException();// The write is normal
          if((_result! =null)) {// The data object is not null
              reply.writeInt(1);// Write 1 to indicate that the serialized data object is not NULL
              _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);// Perform serialization
          }
          else {
            reply.writeInt(0);// Write 0 to identify the serialized data object as NULL
          }
          return true; }}}Copy the code

MethodObject getObjectMethod() and setObjectMethodOut(Out MethodObject) are logically similar, except that when the client deserializes a serialized object written to the server, Since setObjectMethodOut(Out MethodObject) has a data object as an argument, we call readFromParcel() directly to deserialize the data object. MethodObject getObjectMethod() deserializes and recreates an object by calling createFromParcel(). But the effect is the same.

register(CallBackAIDLInterface aidl)

// Client Proxy class
@Override public void register(com.zhukai.aidlservice.CallBackAIDLInterface aidl) throws android.os.RemoteException
{
  // Serialize the operation object Parcel is ready
  android.os.Parcel _data = android.os.Parcel.obtain();
  android.os.Parcel _reply = android.os.Parcel.obtain();
  try {
    _data.writeInterfaceToken(DESCRIPTOR);// Write operator_data.writeStrongBinder((((aidl! =null))? (aidl.asBinder()):(null)));// Write to the Binder object of the AIDl class
    boolean _status = mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0);// Bind calls the server onTransact() function, and the client enters the blocking state
    if(! _status && getDefaultImpl() ! =null) {// Return false if the Binder failed to communicate, or if it succeeded.
      getDefaultImpl().register(aidl);// Invoke the default implementation
      return;
    }
    _reply.readException();// Check whether the server operation is abnormal
  }
  finally {// Release the serialized action object_reply.recycle(); _data.recycle(); }}// Server-side Stub class
@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 TRANSACTION_register:
        {
          data.enforceInterface(descriptor);// Validates the descriptor
          com.zhukai.aidlservice.CallBackAIDLInterface _arg0;
          // Read the Binder object passed in by the client and get the Proxy object via stub.asInterface ()
          _arg0 = com.zhukai.aidlservice.CallBackAIDLInterface.Stub.asInterface(data.readStrongBinder());
          this.register(_arg0);// Call your own method and pass in the proxy object
          reply.writeNoException();// The write is normal
          return true; }}}Copy the code

The register(CallBackAIDLInterface aiDL) function argument is the aiDL class type, which is not passed to the actual logic, but to its own Binder object. The client becomes the server in the role). After reading the incoming Binder object, the server obtains the Proxy object through stub.asInterface (data.readStrongBinder()) function, and then calls its own function to use the Proxy object for the server.

The unregister(CallBackAIDLInterface aidl) function does not need additional explanation, it is the same as register(CallBackAIDLInterface aidl).

Four,

After reading this series, I hope you can have a deeper understanding of AIDL. The length is long. Thank you very much for your patience! Keep reading the AIDL series of articles, and try to learn and output higher quality articles.