# # # introduction
Interprocess communication is important and is ubiquitous in the Android Framework.
When communicating between processes, we often use the AIDL method. Here is a simple example:
First define our AIDL file and create an AIDL file in Android Studio:
The definition is as follows:
package com.nan.testbinder;
interface IAidlInterface {
String pay(int pwd);
}
Copy the code
In this AIDL, we define an interface with a Pay method to simulate alipay payment. The input parameter is the payment password and returns the payment result. After we rebuild the project, AS will automatically generate a Java file for the AIDL file we defined. We won’t go into the details of this file:
Then create our remote service:
import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.nan.testbinder.IAidlInterface; public class MyService3 extends Service { public static final String TAG = MyService3.class.getSimpleName(); public MyService3() { Log.e(TAG, "MyService3"); } @Override public IBinder onBind(Intent intent) { Log.e(TAG, "onBind"); return new Interface(); } @Override public void onCreate() { Log.e(TAG, "onCreate"); } @Override public boolean onUnbind(Intent intent) { Log.e(TAG, "onUnbind"); return super.onUnbind(intent); } @Override public void onDestroy() { Log.e(TAG, "onDestroy"); } public class Interface extends IAidlInterface.Stub { @Override public String pay(int pwd) throws RemoteException { if (PWD == 123) {return "Paid successfully "; } return "payment failed "; }}}Copy the code
In this service, we created an Interface class that inherits the Stub class from the automatically generated Java file. And duplicate the pay method inside. The onBind method of the Service returns an object of this class.
We define this service as a remote service in the manifest file, that is, we specify the process in which the service resides as remote
<service
android:name=".service.MyService3"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
Copy the code
Then define a ServiceConnection class in the Activity to connect to the remote service. Here, the IBinder object is converted into an AIDL interface through Stub’s asInterface method. After the conversion is successful, we try to call the interface’s Pay method and print the payment result through a Toast on success/failure.
class MyRemoteServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) {// The Stub asInterface converts the IBinder reference to the AIDL implementation class mRemoteInterface = IAidlInterface.Stub.asInterface(service); if (mRemoteInterface ! = null) { String result; try { result = mRemoteInterface.pay(123); Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); }} @override public void onServiceDisconnected(ComponentName name) {Copy the code
Finally, bind/unbind the service in the Activity:
mRemoteConn = new MyRemoteServiceConnection(); Intent intent4 = new Intent(MainActivity.this, MyService3.class); // Bind bindService(intent4, mRemoteConn, BIND_AUTO_CREATE); / / unbundling unbindService (mRemoteConn);Copy the code
### Why AIDL?
When we communicate between processes, we cannot get objects of another process’s class, because the two processes are not in the same memory, they are allocated in different virtual machines. That way, even if our class uses static variables and compiles, it won’t be able to communicate.
As shown below, the two processes are like a Chinese and a Japanese, assuming neither knows the other’s language. For example, nowadays, Chinese people send a sentence “don’t” to Japanese, but in Japanese, “don” is “yakramdie”, so it is not normal to communicate (programs can not be accessed directly through variables and functions). So AIDL is our intermediate, for example AIDL is English, assuming that both Chinese and Japanese know English, then they can communicate indirectly.
The following is the basic architecture of cross-process communication:
The Android system allocates a separate block of memory for communication between processes. Once our remote process (service) is started, a reference to IBinder is registered in this memory. However, this reference has no programmatic relationship with the two processes, so it cannot throw objects directly in. It only saves the package name of the service, the class name, what variables the service has, and so on. The presence of a reference to IBinder means that the client can communicate with the service.
Interprocess communication is similar to the C/S architecture, and an AIDL interface includes stubs and proxies, called stubs and proxies respectively. A Stub interacts with a server, while a Proxy interacts with a client. The basic concept of inter-process communication is data read and write. The client writes data to the IBinder, and the Stub reads data from the IBinder. An IBinder is the equivalent of an intermediate server for instant messaging.
### Automatically generated interface class analysis
package com.nan.testbinder; public interface IAidlInterface extends android.os.IInterface { public static abstract class Stub extends Android. OS. Binder implements com. Nan. Testbinder. IAidlInterface {/ / package name private static final Java lang. String DESCRIPTOR = "com.nan.testbinder.IAidlInterface"; public Stub() { this.attachInterface(this, DESCRIPTOR); } public static com.nan.testbinder.IAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin ! = null) && (iin instanceof com.nan.testbinder.IAidlInterface))) { return ((com.nan.testbinder.IAidlInterface) iin); } return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @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_pay: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); java.lang.String _result = this.pay(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.nan.testbinder.IAidlInterface { 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.lang.String pay(int pwd) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(pwd); mRemote.transact(Stub.TRANSACTION_pay, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_pay = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String pay(int pwd) throws android.os.RemoteException; }Copy the code
This interface has two internal classes, Stub and Proxy. Stub will read data from IBinder and Proxy will write data from IBinder.
Where DESCRIPTOR is the package name of the class. Because to ensure that both sides can communicate, you need to make sure that both sides know each other’s class name. private static final java.lang.String DESCRIPTOR = “com.nan.testbinder.IAidlInterface”;
Here is the Stub constructor, which calls the attachInterface function of the parent:
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
Copy the code
Bind this (a reference to IAidlInterface AIDL, which is itself) and DESCRIPTOR to the Binder, so if you’re talking to the same process, like an Activity talking to a Service of the same process, The local interface references are used directly.
When our remote Service is started, an IBinder reference is registered and sent back once the Service binding is successful. But you don’t have to get the IBinder to communicate right away, you actually have to get the object of the AIDL implementation class eventually in order to communicate. That is, take the IBinder and generate the implementation class of AIDL that we need according to the DESCRIPTOR.
For example, in the chestnut above:
/ / by a Stub asInterface convert to IBinder reference to the implementation of an AIDL interface class mRemoteInterface = IAidlInterface. Stub. AsInterface (service);Copy the code
If the same process is communicating with each other, then the local AIDL class reference is implemented directly, so the following code is to find the local reference through IBinder’s queryLocalInterface method.
public static com.nan.testbinder.IAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; IInterface iin = obj. QueryLocalInterface (DESCRIPTOR); if (((iin ! = null) && (iin instanceof com.nan.testbinder.IAidlInterface))) { return ((com.nan.testbinder.IAidlInterface) iin); } return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj); }Copy the code
Since IBinder is just an interface, Binder’s source code is needed to see the queryLocalInterface implementation:
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
Copy the code
If the DESCRIPTOR DESCRIPTOR DESCRIPTOR is the same as the DESCRIPTOR DESCRIPTOR DESCRIPTOR DESCRIPTOR, it is the same process. Return the local AIDL implementation (mOwner).
If not, then it belongs to a different process, then we need to initialize our AIDL object via IBinder reference:
return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj);
Copy the code
Now we need to look at the Proxy constructor:
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
Copy the code
The Proxy class does not inherit from Binder but implements AIDL’s interface directly:
private static class Proxy implements com.nan.testbinder.IAidlInterface
Copy the code
In other words, Proxy construction is the process by which IBinder references the implementation class of the interface converted to AIDL.
Continuing with Stub’s onTransact method:
@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_pay: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _result = this.pay(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
Copy the code
The Stub is the server to which the IBinder writes data. The onTransact method is called back when communicating.
Interprocess communication is nothing more than “indirect calls to functions”. Here is the pay method I defined in AIDL. If the client pay method is called, then the remote corresponding method is also called.
The process starts with the client writing data to the IBinder via Proxy, and the Parcel is related to serialization.
@Override public java.lang.String pay(int pwd) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; // Write the description and data to a Parcel object. The description controls which object data will be sent to in the future. _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(pwd); // Call IBinder transact to connect to the remote Stub object. Transact (stub.transaction_pay, _data, _reply, 0); // If there is an exception _reply.readexception (); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; }Copy the code
Let’s look at the implementation of the Transact method in Binder
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); } boolean r = onTransact(code, data, reply, flags); if (reply ! = null) { reply.setDataPosition(0); } return r; }Copy the code
Pooling concept, fifO queue approach:
public static Parcel obtain() { final Parcel[] pool = sOwnedPool; synchronized (pool) { Parcel p; for (int i=0; i<POOL_SIZE; i++) { p = pool[i]; if (p ! = null) { pool[i] = null; if (DEBUG_RECYCLE) { p.mStack = new RuntimeException(); } return p; } } } return new Parcel(0); }Copy the code
The onTransact method of the remote Stub implements:
@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_pay: {// Pass in the DESCRIPTOR as validation, if the DESCRIPTOR is the same as the local DESCRIPTOR, proceed. data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); Java.lang.String _result = this.pay(_arg0); // Return the result to IBinder and pass it back to the client reply.writenoException (); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); }Copy the code
###bindService source code analysis
To look at bindService, you need to find the ContextImpl implementation of the Context.
@Override public boolean bindService(Intent service, ServiceConnection conn, int flags) { warnIfCallingFromSystemProcess(); // It is related to the security of the system, if you call from the system, Return bindServiceCommon(service, conn, flags, mmainThread.gethandler (), process.myUserHandle ()); }Copy the code
###### You can open the class inheritance diagram in Android Studio by pressing Ctrl+H.
BindService calls bindServiceCommon.
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { try { service.prepareToLeaveProcess(this); // Core code, Started interprocess communication int res = ActivityManagerNative. GetDefault () bindService (mMainThread. GetApplicationThread (), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier()); return res ! = 0; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }}Copy the code
ActivityManagerNative inherits Binder and implements the IActivityManager interface, so it is relevant for process communication.
public abstract class ActivityManagerNative extends Binder implements IActivityManager{
}
Copy the code
contrast
public static abstract class Stub extends android.os.Binder implements com.nan.testbinder.IAidlInterface{
}
Copy the code
Notice that ActivityManagerNative is a Stub object, and if you look closely at this class, you’ll see the following Proxy. Communicate with system services.
Continuing analysis:
static public IActivityManager getDefault() {
return gDefault.get();
}
Copy the code
Take a reference to the system service IBinder and use the asInterface method to convert the IBinder reference to the desired IActivityManager interface (essentially the AIDL interface) :
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() { protected IActivityManager the create () {/ / by IBinder b = ServiceManager. GetService (" activity "); IActivityManager am = asInterface(b); return am; }};Copy the code
So this code actually uses interprocess communication to fetch the system service named “Activity” from the ServiceManager and convert the IBinder reference to the desired IActivityManager interface using the asInterface method. The referenced bindService method is then called.
ActivityManagerNative. GetDefault () / / this is a ActivityManagerService (AMS) bindService ();Copy the code
###### System services, communication between processes
ServiceManager- Manages all system services. A ServiceManager acts as a console, and we can get an AIDL reference to the corresponding service by making a phone call (our process communicates with the system process).
All system services need to register their own references with the ServiceManager when they start: that is, they reference their IBinder to the ServiceManager. Stubs created by AIDL are deregistered through the NDK by calling the parent Binder constructor.
The implementation class of IActivityManager is ActivityManagerService (AMS). AMS is an important class for the four components to communicate between processes. (Because it implements the aiDL-like interface: ActivityManagerNative)
public final class ActivityManagerService extends ActivityManagerNative implements ... {}Copy the code
In AMS, bindService is implemented in this way. Note: It is already in the system service (AMS) process. Continued by the system process:
public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException { enforceNotIsolatedCaller("bindService"); Synchronized (this) {// Finally, bindServiceLocked through the mServices (ActiveService) object, Here the interprocess communication return mServices. BindServiceLocked (caller, token, service, resolvedType, connection, flags, callingPackage, userId); }}Copy the code
Finally, bindServiceLocked via the mServices (ActiveServices) object, which starts interprocess communication. This is a long method, just look at the core code:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, String callingPackage, Final int userId) throws TransactionTooLargeException {/ / final judgment process ProcessRecord callerApp = mAm.getRecordForAppLocked(caller); ActivityRecord = null; ActivityRecord = null; ActivityRecord = null; if (token ! = null) { activity = ActivityRecord.isInStackLocked(token); if (activity == null) { Slog.w(TAG, "Binding with unknown activity: " + token); return 0; } } int clientLabel = 0; PendingIntent clientIntent = null; final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID; If (isCallerSystem) {service.setdefusable (true); clientIntent = service.getParcelableExtra(Intent.EXTRA_CLIENT_INTENT); if (clientIntent ! = null) { clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0); if (clientLabel ! = 0) { service = service.cloneFilter(); If ((flags&Context.bind_treat_like_activity)! = 0) { mAm.enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, "BIND_TREAT_LIKE_ACTIVITY"); } // This is related to the Service lifecycle ServiceLookupResult res = retrieveServiceLocked(Service, resolvedType, callingPackage, Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg, isBindExternal); try { AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent); IBinder binder = connection.asbinder (); IBinder binder = connection.asbinder (); ArrayList<ConnectionRecord> clist = s.connections.get(binder); if (clist == null) { clist = new ArrayList<ConnectionRecord>(); s.connections.put(binder, clist); } clist.add(c); if (s.app ! Received) {try {// Connect to MainActivity C.onn. connected(s.name, B.intent.binder); } catch (Exception e) {} Will call here the if (b.i ntent. Apps. The size () = = 1 && b.i ntent. DoRebind) {requestServiceBindingLocked (s, b.i ntent, callerFg, true); } } else if (! b.intent.requested) { requestServiceBindingLocked(s, b.intent, callerFg, false); } getServiceMap(s.userId).ensureNotStartingBackground(s); } finally { Binder.restoreCallingIdentity(origId); } return 1; }Copy the code
And then we see ActiveServices requestServiceBindingLocked in implementation, Start the other process’s ActivityThread with forceProcessStateUpTo (that is, the main method of the ActivityThread is called via C++ and the message loop is started). Start the process first, and then start the service in the process, no problem! The start of ApplicationThread represents the start of the APP.
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException { if ((! i.requested || rebind) && i.apps.size() > 0) { try { bumpServiceExecutingLocked(r, execInFg, "bind"); / / start ActivityThread of state Richard armitage pp. ForceProcessStateUpTo (ActivityManager. PROCESS_STATE_SERVICE); //app is a ServiceRecord member variable ProcessRecord, Thread is a member variable of ProcessRecord, IApplicationThread, Is the core of the Android components start class of state Richard armitage pp. Thread. ScheduleBindService (r, i.i ntent, getIntent (), the rebind of state Richard armitage pp. RepProcState); if (! rebind) { i.requested = true; } i.hasBound = true; i.doRebind = false; } catch (TransactionTooLargeException e) { } catch (RemoteException e) { } } return true; }Copy the code
The IApplicationThread implementation class is the core class for the startup of Android components. It is also related to the communication between processes.
public interface IApplicationThread extends IInterface {
}
public abstract class ApplicationThreadNative extends Binder implements IApplicationThread {
}
private class ApplicationThread extends ApplicationThreadNative {
}
Copy the code
The IApplicationThread implementation class is an inner class of ActivityThread: ApplicationThread!!
So calling ApplicationThread’s bindService starts another ActivityThread and sends a message to the system Handler (H) that starts the Service:
public final void scheduleBindService(IBinder token, Intent intent, boolean rebind, int processState) { updateProcessState(processState, false); // Save some parameters about Service BindServiceData s = new BindServiceData(); s.token = token; s.intent = intent; s.rebind = rebind; // Send a message to the system Handler and start Service sendMessage(h.bin_service, s); }Copy the code
After the system Handler receives the message:
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
Copy the code
HandleBindService:
Private void handleBindService(BindServiceData data) {//mServices are created by reflection when handleCreateService s = mServices.get(data.token); if (s ! = null) { try { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(); try { if (! IBinder binder = s.onbind (data.intent); ActivityManagerNative.getDefault().publishService( data.token, data.intent, binder); } else { s.onRebind(data.intent); // Then through interprocess communication, Put the service object of IBinder Binder driver inside ActivityManagerNative. GetDefault () serviceDoneExecuting (data) token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } ensureJitEnabled(); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } catch (Exception e) { if (! mInstrumentation.onException(s, e)) { throw new RuntimeException( "Unable to bind to service " + s + " with " + data.intent + ": " + e.toString(), e); }}}}Copy the code
Once you get there, you call back to ServiceConnection’s onServiceConnected method, and once you get the IBinder reference, you need to create a reference to the AIDL interface for communication.
class MyRemoteServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { mRemoteInterface = IAidlInterface.Stub.asInterface(service); if (mRemoteInterface ! = null) { String result; try { result = mRemoteInterface.pay(123); Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); }} @override public void onServiceDisconnected(ComponentName name) {Copy the code
Initialize the Stub first, while it is being constructed, and save the description.
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
Copy the code
When we bindService, we first find the system service, which creates the corresponding process and service, and then returns the IBinder when we call back. A Service is created by reflection. When an AIDL reference is created, Stub constructors are called to create stubs. Since stubs inherit from Binder, according to Java syntax, the Binder init method is called first. Register IBinder references to memory dedicated to process communication:
public Binder() { init(); if (FIND_POTENTIAL_LEAKS) { final Class<? extends Binder> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Binder class should be static or leaks might occur: " + klass.getCanonicalName()); }}}Copy the code
Where init is a native method:
private native final void init();
Copy the code
The IBinder is registered in this init method.
We then convert the IBinder reference to the AIDL interface implementation class by manually calling Stub’s asInterface:
/ / by a Stub asInterface convert to IBinder reference to the implementation of an AIDL interface class mRemoteInterface = IAidlInterface. Stub. AsInterface (service);Copy the code
Select IBinder, IBinder, IBinder, IBinder, IBinder, IBinder, IBinder
public static com.nan.testbinder.IAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; IInterface iin = obj. QueryLocalInterface (DESCRIPTOR); if (((iin ! = null) && (iin instanceof com.nan.testbinder.IAidlInterface))) { return ((com.nan.testbinder.IAidlInterface) iin); } return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj); }Copy the code
Once MainActivity gets the reference, we can call our own pay method for interprocess communication.
How to write the data into IBinder?
We know that data is written to IBinder via Proxy, such as the code we just saw:
@Override public java.lang.String pay(int pwd) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(pwd); MRemote. Transact (stub.transaction_pay, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; }Copy the code
This is our custom pay method in our custom AIDL class. Here we take a closer look at mremote.transact (…). This method. First of all, from the above analysis, mRemote is passed in when the Proxy is constructed (in a different process). ###### Again, Binder is itself if it’s the same process, because the Stub class itself inherits Binder.
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.nan.testbinder.IAidlInterface asInterface(android.os.IBinder obj) {
//...
return new com.nan.testbinder.IAidlInterface.Stub.Proxy(obj);
}
Copy the code
So if we look at transact implementation, we actually look at its implementation class Binder inner class BinderProxy transact:
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
if (Binder.isTracingEnabled()) {
Binder.getTransactionTracker().addTrace();
}
return transactNative(code, data, reply, flags);
}
Copy the code
TransactNative method is called in IBinder, that is to say, the underlying driver of IBinder is realized through C++, and the data is written into the IBinder driver through the underlying code.
Binder’s onTransact function dumps the data back:
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { if (code == INTERFACE_TRANSACTION) { reply.writeString(getInterfaceDescriptor()); return true; } else if (code == DUMP_TRANSACTION) { ParcelFileDescriptor fd = data.readFileDescriptor(); String[] args = data.readStringArray(); if (fd ! = null) {try {// Dump (fd.getFileDescriptor(), args); } finally { IoUtils.closeQuietly(fd); } } // Write the StrictMode header. if (reply ! = null) { reply.writeNoException(); } else { StrictMode.clearGatheredViolations(); } return true; } else if (code == SHELL_COMMAND_TRANSACTION) { ParcelFileDescriptor in = data.readFileDescriptor(); ParcelFileDescriptor out = data.readFileDescriptor(); ParcelFileDescriptor err = data.readFileDescriptor(); String[] args = data.readStringArray(); ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data); try { if (out ! = null) { shellCommand(in ! = null ? in.getFileDescriptor() : null, out.getFileDescriptor(), err ! = null ? err.getFileDescriptor() : out.getFileDescriptor(), args, resultReceiver); } } finally { IoUtils.closeQuietly(in); IoUtils.closeQuietly(out); IoUtils.closeQuietly(err); // Write the StrictMode header. if (reply ! = null) { reply.writeNoException(); } else { StrictMode.clearGatheredViolations(); } } return true; } return false; }Copy the code
When is the IBinder reference placed in shared memory?
The Stub constructor in AIDL calls its parent Binder constructor and init method in NDK for registration.
### source code analysis related
There are two methods of source code analysis: architecture analysis (class relationships) and flow analysis
Source code analysis needs to constantly find the implementation of the interface class source code analysis recommended learning sequence: Binder, Handler message mechanism, VIew, event distribution, system service (ServiceManager represents system service, AMS represents application startup process), architecture of the four components, WindowManager, learning system application source code (Launcher, etc.)
If you feel that my words are helpful to you, welcome to pay attention to my public number:
My group welcomes everyone to come in and discuss all kinds of technical and non-technical topics. If you are interested, please add my wechat huannan88 and I will take you into our group.