This article source analysis based on Android Q

This is a short passage that illustrates a simple but often overlooked knowledge point.

Chairman MAO told us in practice: “If you want to know the taste of pears, you have to change pears and eat them yourself.” This translates into programmer jargon as’ Read the fucking source code ‘.

The same is true of the concept of inout in AIDL. The direct reason for many people’s confusion is that they have not personally seen the Java files generated by AIDL-Gen. When I think about it, there are probably two things that prevent people from directly “transforming” AIDL:

  1. The Java file needs to be compiled and generated and is not in the source code.
  2. Some programmers who are used to writing simple Java classes may find the relationships between abstract classes, static inner classes, and interfaces in the AiDL-Gen generated Java files a bit complicated (there’s a reason you chose to use abstract classes, static inner classes).

The easiest way to understand in/out/inout is not to talk about it in paragraphs, but to find an AIDL-generated Java file and experience it firsthand.

The AIDL file we chose here is iaidltest.aidl.

frameworks/base/core/tests/coretests/src/android/os/IAidlTest.aidl

18 package android.os; 19 20 import android.os.AidlTest; 21 22 interface IAidlTest { 23 int intMethod(int a); 24 25 AidlTest.TestParcelable parcelableIn(in AidlTest.TestParcelable p); 26 AidlTest.TestParcelable parcelableOut(out AidlTest.TestParcelable p); 27 AidlTest.TestParcelable parcelableInOut(inout AidlTest.TestParcelable p); 28 29 AidlTest.TestParcelable listParcelableLonger( 30 inout List<AidlTest.TestParcelable> list, int index); 31 int listParcelableShorter( 32 inout List<AidlTest.TestParcelable> list, int index); 33 34 boolean[] booleanArray(in boolean[] a0, out boolean[] a1, inout boolean[] a2); 35 char[] charArray(in char[] a0, out char[] a1, inout char[] a2); 36 int[] intArray(in int[] a0, out int[] a1, inout int[] a2); 37 long[] longArray(in long[] a0, out long[] a1, inout long[] a2); 38 float[] floatArray(in float[] a0, out float[] a1, inout float[] a2); 39 double[] doubleArray(in double[] a0, out double[] a1, inout double[] a2); 40 String[] stringArray(in String[] a0, out String[] a1, inout String[] a2); 41 AidlTest.TestParcelable[] parcelableArray(in AidlTest.TestParcelable[] a0, 42 out AidlTest.TestParcelable[] a1, 43 inout AidlTest.TestParcelable[] a2); 44 45 void voidSecurityException(); 46 int intSecurityException(); 47}Copy the code

Here we’ll focus on just three methods:

  • parcelableIn
  • parcelableOut
  • parcelableInOut

Aidltest. TestParcelable is a class that inherits the Parcelable interface. The methods in this class do two things:

  1. Converts structured data from a class into serialized data that is written to a Parcel object for subsequent Binder drivers to copy to other processes.
  2. Copy back a Parcel object from another process and rebuild a structured Java object with its serialized data.

1. parcelableIn

All cross-process communication is initiated by the client side, so we need to focus on the Proxy class generated in the Java file, which is the class of the Java objects that the client process obtains for communication.

out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frame works/base/core/tests/coretests/src/android/os/IAidlTest.java

463      @Override public android.os.AidlTest.TestParcelable parcelableIn(android.os.AidlTest.TestParcelable p) throws android.os.RemoteException
464      {
465        android.os.Parcel _data = android.os.Parcel.obtain();
466        android.os.Parcel _reply = android.os.Parcel.obtain();
470        android.os.AidlTest.TestParcelable _result;
468        try {
469          _data.writeInterfaceToken(DESCRIPTOR);
470          if((p! =null)) {
471            _data.writeInt(1);
472            p.writeToParcel(_data, 0);
473          }
474          else {
475            _data.writeInt(0);
476          }
477          boolean _status = mRemote.transact(Stub.TRANSACTION_parcelableIn, _data, _reply, 0);
478          if(! _status && getDefaultImpl() ! =null) {
479            return getDefaultImpl().parcelableIn(p);
480          }
481          _reply.readException();
482          if ((0! =_reply.readInt())) {483            _result = android.os.AidlTest.TestParcelable.CREATOR.createFromParcel(_reply);
484          }
485          else {
486            _result = null;
487          }
488        }
489        finally {
490          _reply.recycle();
491          _data.recycle();
492        }
493        return _result;
494      }
Copy the code

To transmit the parameters to the peer process, you need to write them to a Parcel object first, line 472 of the code above. True cross-process communication is then conducted via mremote.transact.

Binder transfers data as an in-memory copy, so instead of holding TestParcelable objects, the peer process gets a “mirror” copy of its template. The “in” modifier p has only one purpose: to produce a “mirror” of P in the peer process. No matter how the data of this “mirror” changes thereafter, p will not be affected.

2. parcelableOut

out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frame works/base/core/tests/coretests/src/android/os/IAidlTest.java

495      @Override public android.os.AidlTest.TestParcelable parcelableOut(android.os.AidlTest.TestParcelable p) throws android.os.RemoteException
496      {
497        android.os.Parcel _data = android.os.Parcel.obtain();
498        android.os.Parcel _reply = android.os.Parcel.obtain();
499        android.os.AidlTest.TestParcelable _result;
500        try {
501          _data.writeInterfaceToken(DESCRIPTOR);
502          boolean _status = mRemote.transact(Stub.TRANSACTION_parcelableOut, _data, _reply, 0);
503          if(! _status && getDefaultImpl() ! =null) {
504            return getDefaultImpl().parcelableOut(p);
505          }
506          _reply.readException();
507          if ((0! =_reply.readInt())) {508            _result = android.os.AidlTest.TestParcelable.CREATOR.createFromParcel(_reply);
509          }
510          else {
511            _result = null;
512          }
513          if ((0! =_reply.readInt())) {514            p.readFromParcel(_reply);
515          }
516        }
517        finally {
518          _reply.recycle();
519          _data.recycle();
520        }
521        return _result;
522      }
Copy the code

You can see that until line 502, only DESCRIPTOR was written to _data (used to verify data received by the peer process), so the parameter p modified with “out” is not transmitted to the peer process.

When the peer process returns data, all returned data is serialized in _reply. First line 508 reads the data from _reply and creates the return object (which is the method return value). The next 514 lines read the data from _reply and save it to parameter P, which is equivalent to the peer process updating its own data.

The original data in p will not be passed to the peer process, but the data in p will be updated by the peer process after it returns from this method. So you can think of it as the second return value, which is why you use the “out” modifier.

3. parcelableInOut

out/soong/.intermediates/frameworks/base/core/tests/coretests/FrameworksCoreTests/android_common/xref/srcjars.xref/frame works/base/core/tests/coretests/src/android/os/IAidlTest.java

523      @Override public android.os.AidlTest.TestParcelable parcelableInOut(android.os.AidlTest.TestParcelable p) throws android.os.RemoteException
524      {
525        android.os.Parcel _data = android.os.Parcel.obtain();
526        android.os.Parcel _reply = android.os.Parcel.obtain();
527        android.os.AidlTest.TestParcelable _result;
528        try {
529          _data.writeInterfaceToken(DESCRIPTOR);
530          if((p! =null)) {
531            _data.writeInt(1);
532            p.writeToParcel(_data, 0);
533          }
534          else {
535            _data.writeInt(0);
536          }
537          boolean _status = mRemote.transact(Stub.TRANSACTION_parcelableInOut, _data, _reply, 0);
538          if(! _status && getDefaultImpl() ! =null) {
539            return getDefaultImpl().parcelableInOut(p);
540          }
541          _reply.readException();
542          if ((0! =_reply.readInt())) {543            _result = android.os.AidlTest.TestParcelable.CREATOR.createFromParcel(_reply);
544          }
545          else {
546            _result = null;
547          }
548          if ((0! =_reply.readInt())) {549            p.readFromParcel(_reply);
550          }
551        }
552        finally {
553          _reply.recycle();
554          _data.recycle();
555        }
556        return _result;
557      }
Copy the code

After reading the meanings of “in” and “out”, the meaning of “inout” is self-evident. The data in parameter p is passed to and updated by the data returned by the peer end. This is the same as when a local call passes in a reference parameter: the passed parameter makes its data known to the method body, and changes made to the data inside the passed parameter are also known to the outside world.