AIDL: How do clients get this remote control? 2. How does this remote control call server code? 3. Even if the server app is killed, why can the client still call the server code?

We get the iLeoAidl object from the client as an entry point to the source code analysis

 private void bindService() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.xx.leo_service"."com.xx.leo_service.LeoAidlService"));
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }
Copy the code
 private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "onServiceConnected: success");
            iLeoAidl = ILeoAidl.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "onServiceDisconnected: success"); iLeoAidl = null; }};Copy the code

The client binds the service to the onServiceConnected listener using the bindService method, and then binds the IBinder’s service object, Introduced to ILeoAidl. Stub. AsInterface access to ILeoAidl object method. So we have two sources to analyze

  • BindService (intent, connection, context.bind_auto_create); How is it bound?
  • 2, ILeoAidl. Stub. AsInterface (service); How do I get AIDL objects? Let’s first look at how the client gets the AIDL object. Let’s click on the source code
public interface ILeoAidl extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.xx.leo_service.ILeoAidl {
        private static final java.lang.String DESCRIPTOR = "com.xx.leo_service.ILeoAidl";

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

        /**
         * Cast an IBinder object into an com.xx.leo_service.ILeoAidl interface,
         * generating a proxy if needed.
         */
        public static com.xx.leo_service.ILeoAidl asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if(((iin ! = null) && (iin instanceof com.xx.leo_service.ILeoAidl))) {return ((com.xx.leo_service.ILeoAidl) iin);
            }
            return new com.xx.leo_service.ILeoAidl.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_addPerson: {
                    data.enforceInterface(DESCRIPTOR);
                    com.xx.leo_service.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.xx.leo_service.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPerson(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPersonList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.xx.leo_service.Person> _result = this.getPersonList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true; }}return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.xx.leo_service.ILeoAidl {
            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 void addPerson(com.xx.leo_service.Person person) 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((person ! = null)) { _data.writeInt(1); person.writeToParcel(_data, 0); }else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

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

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

    public void addPerson(com.xx.leo_service.Person person) throws android.os.RemoteException;

    public java.util.List<com.xx.leo_service.Person> getPersonList() throws android.os.RemoteException;
}

Copy the code

In fact, the ILeoAidl interface is a template generated by the system through the AIDL file we wrote (so the client and server app will have this class). Let’s first look at the structure of this interface

  • Stub: Used to receive data, abstract classes, and implement ILeoAidl
  • Proxy: an entity class that sends data and implements ILeoAidl

Stub is the internal class of the ILeoAidl interface, and Proxy is the internal class of the Stub. Let’s go directly to the sInterface method of the Stub and see how the client obtains the AIDL object.

        public static abstract class Stub extends android.os.Binder implements com.xx.leo_service.ILeoAidl {
        private static final java.lang.String DESCRIPTOR = "com.xx.leo_service.ILeoAidl";

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

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

The first thing it does is it says, is the service that’s passed in is empty, and if it’s empty, it just returns, and then it goes to obj. QueryLocalInterface (DESCRIPTOR), So we see that DESCRIPTOR is “com.xx.leo_service.ileoaidl” which is the full class name of aiDL. Binder’s queryLocalInterface method is implemented in Binder’s queryLocalInterface method

 public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }
Copy the code

It compares whether the descriptor that’s passed in is equal to the mDescriptor, in which case it returns a mOwner member variable of type IInterface; We see the stub constructor again

 public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
Copy the code

  public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }
Copy the code

So you’re going to assign a DESCRIPTOR to an mDescriptor at the beginning, and you’re going to assign this to the owner, is that going to be equal at the beginning? Is not equal, because asInterface is a static method, will not go constructor, so through ILeoAidl. Stub. AsInterface (service); This way in, the mDescriptor is empty, there’s no assignment. Now let’s look at the code for our server app,

 btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    iLeoAidl.addPerson(new Person("Little grandpa", 3)); List<Person> persons = iLeoAidl.getPersonList(); Log.e(TAG,persons.size()+persons.toString()); } catch (RemoteException e) { e.printStackTrace(); }}});Copy the code

So iLeoAidl. AddPerson is calling addPerson in Proxy so let’s look at the addPerson method in Proxy

 @Override
            public void addPerson(com.xx.leo_service.Person person) 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((person ! = null)) { _data.writeInt(1); person.writeToParcel(_data, 0); }else{ _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); }}Copy the code

_data is used to store data sent to the server. _reply is used to store the data returned by the server, and _data.writeInterfaceToken(DESCRIPTOR) is used to verify, because one process can call multiple services. Each service has an AIDL corresponding to it), so to check the native method, we will not look at it, and then write data to _data, Finally, call mremote. transact(stub.transaction_addperson, _data, _reply, 0); At this point, the client thread is suspended until the server returns data and the client thread continues running. Stub.transaction_addperson is the integer that marks the location of the method, because both the client and the server know the full class name of all the methods in the.aidl, so we just mark which method is in which location. Let’s click on the Transact method

   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

We come to the Transact method for Binder, which we can now understand as: Calling mremote. transact calls Binder’s Transact method. Binder then calls the Stub onTransact in the server aiDL through a bunch of processing to receive data, so let’s look at the server’s onTransact method first

 @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_addPerson: {
                    data.enforceInterface(DESCRIPTOR);
                    com.xx.leo_service.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.xx.leo_service.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPerson(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getPersonList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.xx.leo_service.Person> _result = this.getPersonList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true; }}return super.onTransact(code, data, reply, flags);
        }
Copy the code

We see that TRANSACTION_addPerson, which is the method that adds the person, initially checks it, and then gets the data and assigns it to this.addPerson(_arg0); The this.addPerson method calls the addPerson method in the LeoAidlService that the server wrote.

Now let’s analyze the whole process through the above flow chart

AddPerson is called into Proxy by the client. Proxy calls mremote. transact into Binder. Binder calls onTransact into the server Stub 4. Calling this.addPerson from the Stub onTransact on the server will run to the server’s own LeoAidlService

BindService (Intent, connection, context.bind_auto_create); How are client and server bound?

Before analyzing this problem, let’s talk about the understanding of some services in the Android system, such as alarm clock service and call service. In fact, our APP access to these services is also cross-process. So there are so many services in the system, how does the Android system manage them? The ServiceManager is used to manage services in the system, and the ServiceManager itself is also a service. Therefore, when we invoke alarm services, the ServiceManager is first invoked by proxy. Then the ServiceManager goes to the alarm services and binds them to us so that we can call some of the methods in the alarm services. So we go to find ServiceManager is also through AIDL, so the analysis of the following source code to be combined with the previous AIDL process.

Now let’s get into the analysis of the binding process by clicking on the bindService method

 @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }
Copy the code

So if we go to ContextWrapper’s bindService method, it’s actually calling Context’s bindService, and we know that Context is an abstract class, and we all know that the implementation is in ContextImpl. So we see the bindService method here in ContextImpl


 @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, Process.myUserHandle());
    }
Copy the code

We see it’s going to bindServiceCommon

  private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            UserHandle user) {
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if(mPackageInfo ! = null) { sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), mMainThread.getHandler(), flags); }else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if(token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo ! = null && mPackageInfo.getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } service.prepareToLeaveProcess(); int res = ActivityManagerNative.getDefault().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier());if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            returnres ! = 0; } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); }}Copy the code

We see that a piece of code int. Res = ActivityManagerNative getDefault (). The bindService I look forward to getDefault method,

  static public IActivityManager getDefault() {
        return gDefault.get();
    }
Copy the code

We see that it’s returning an IActivityManager, so we click on IActivityManager, and we see that the ActivityManager inherits from IInterface. The aiDL file ILeoAidl generated by android system is also inherited from IInterface when analyzing the flow. So we get. Through the aidl to find ServiceManager # # # IActivityManager analogy ILeoAidl we know ILeoAidl interface is a stub, proxy, inner class, in here, we don’t see ah, so we are back to getDefault, Let’s look at getDefault


  private static final Singleton<IActivityManager> getDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager"."default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager"."default service = " + am);
            }
            returnam; }};Copy the code

IActivityManager am = asInterface(b); Before we are through ILeoAidl. Stub. AsInterface (service); IActivityManager am is a Proxy instance object, getDefault is a Proxy instance object, and asInterface is a Stub method

public abstract class ActivityManagerNative extends Binder implements IActivityManager { /** * Cast a Binder object into  an activity manager interface, generating * a proxyif needed.
     */
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in! = null) {return in;
        }

        return new ActivityManagerProxy(obj);
    }
Copy the code

ActivityManagerNative is a Stub that inherits from Binder and implements the AIDL interface. return new ActivityManagerProxy(obj); So we see the structure diagram of Active ManagerNative

class ActivityManagerProxy implements IActivityManager { public ActivityManagerProxy(IBinder remote) { mRemote = remote;  }Copy the code

ActivityManagerProxy implements IActivityManager, Is to realize the aidl interface # # # ActivityManagerProxy analogy Proxy so we go back to the int res = ActivityManagerNative. GetDefault (). The bindService this code, Because ActivityManagerNative. GetDefault (). Is an instance object that returns Proxy, so look for the bindService method in ActivityManagerProxy

public int bindService(IApplicationThread caller, IBinder token,
            Intent service, String resolvedType, IServiceConnection connection,
            int flags,  String callingPackage, int userId) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller! = null ? caller.asBinder() : null); data.writeStrongBinder(token); service.writeToParcel(data, 0); data.writeString(resolvedType); data.writeStrongBinder(connection.asBinder()); data.writeInt(flags); data.writeString(callingPackage); data.writeInt(userId); mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0); reply.readException(); int res = reply.readInt(); data.recycle(); reply.recycle();return res;
    }
Copy the code

Does this bindService method look a lot like our addPerson method? Get data and reply first, stuff data into data, then checksum descriptor, then call mremote. transact(BIND_SERVICE_TRANSACTION, data, reply, 0); Method in the server Stub, so we find the case condition for ActivityManagerNative’s onTransact method BIND_SERVICE_TRANSACTION

 case BIND_SERVICE_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            IBinder token = data.readStrongBinder();
            Intent service = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            b = data.readStrongBinder();
            int fl = data.readInt();
            String callingPackage = data.readString();
            int userId = data.readInt();
            IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
            int res = bindService(app, token, service, resolvedType, conn, fl,
                    callingPackage, userId);
            reply.writeNoException();
            reply.writeInt(res);
            return true;
        }

Copy the code

Int res = bindService(app, token, service, resolvedType, conn, fl,call package, userId); In this section, we know that the stub onTransact is calling the real service, just like we called the real addPerson method in LeoAidlService. The core service of Android system is AMS (ActivityManagerService), so here bindService method is actually to call AMS bindService, source code analysis here, equivalent to the previous said, We use the ServiceManager to help us find the alarm service, and our actual example is LeoAidlService, so we find the service we are looking for, the next step is to analyze how to communicate with the service. ##### Therefore, we will analyze this service in four states:

  • 1. App (process) is not started
  • 2. App (process) started, service not created
  • 3. App (process) starts, service is created, service is not bound
  • The app starts, the service is created, and the service is already bound. ###ActivityManagerService analogy LeoAidlService because the real service is in AMS, so we see bindService in AMS
public int bindService(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags, String callingPackage,
            int userId) throws TransactionTooLargeException {
        enforceNotIsolatedCaller("bindService");

        // Refuse possible leaked file descriptors
        if(service ! = null && service.hasFileDescriptors() ==true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        synchronized(this) {
            return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId); }}Copy the code

Let’s go to ActiveServices#bindServiceLocked

  int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, IServiceConnection connection, int flags,
            String callingPackage, int userId) throws TransactionTooLargeException {
          .......
         bringUpServiceLocked(s, service.getFlags(), callerFg, false)... requestServiceBindingLocked(s, b.intent,callerFg, false); . }Copy the code

Too much content, because bindServiceLocked method we directly bringUpServiceLocked pulled out two of the most core method and requestServiceBindingLocked, Let’s look at the bringUpServiceLocked method first

  private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting) throws TransactionTooLargeException {
        ......
      if(app ! = null && app.thread ! = null) { try { app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats); realStartServiceLocked(r, app,execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                }

                // If a dead object exception was thrown -- fall through to
                // restart the application.
         }
        ......
  if (app == null) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    "service", r.name, false, isolated, false)) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                bringDownServiceLocked(r);
                return msg;
            }
            if(isolated) { r.isolatedProc = app; }}... }Copy the code

App ==null: app is not started. 2. One is if (app! = null && app.thread ! = null) {this check means that the app has been started. ###1, the app has been started, then the ActiveServices#realStartServiceLocked method is called.

 private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException { ...... app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); . }Copy the code

We only see the main method of app. Thread. ScheduleCreateService, then the app. What is the thread? We look at the ProcessRecord app, and we know that when we start an app we create a process, so that process is stored in ProcessRecord. App. Thread is the IApplicationThread.

    public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;

            sendMessage(H.CREATE_SERVICE, s);
        }
Copy the code

We see sendMessage(h.create_service, s); So we go directly to the handleMessage where handle handles the message according to CREATE_SERVICE

           case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
Copy the code

Enter the handleCreateService method

 private void handleCreateService(CreateServiceData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            if(! mInstrumentation.onException(service, e)) { throw new RuntimeException("Unable to instantiate service " + data.info.name
                    + ":" + e.toString(), e);
            }
        }

        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
            mServices.put(data.token, service);
            try {
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                // nothing to do.
            }
        } catch (Exception e) {
            if(! mInstrumentation.onException(service, e)) { throw new RuntimeException("Unable to create service " + data.info.name
                    + ":"+ e.toString(), e); }}}Copy the code

Here is the place where create real service, also is the place where we create LeoAidlService, see these two lines of code. Java lang. This cl = packageInfo. GetClassLoader (); service = (Service) cl.loadClass(data.info.name).newInstance(); So it’s really just a matter of getting some information about the application, using reflection and class loaders to create it, calling service.oncreate () to start the service, and then putting the service into the mServices array. LeoAidlService is a service created by reflection, so even if the server app is killed, the client can still call the server code. After analyzing the case that our app has started, we are now analyzing the case that our app has not started yet. We go to ActiveServices#startProcessLocked

    private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {



        if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
      }
Copy the code

We see the main code directly, it hand in the “android. App. ActivityThread” through the Process. The start directly to create a Process. So if the app doesn’t start, he’s going to give you a process to create the app. LeoAidlService has been created. The next step is to analyze how LeoAidlService is bound to the client.

So we’ll see requestServiceBindingLocked method

# ActiveServices# requestServiceBindingLocked (2, 3)

    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind. return false; } if ((! i.requested || rebind) && i.apps.size() > 0) { try { bumpServiceExecutingLocked(r, execInFg, "bind"); r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE); r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState); if (! rebind) { i.requested = true; } i.hasBound = true; i.doRebind = false; } catch (TransactionTooLargeException e) { // Keep the executeNesting count accurate. if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e); final boolean inDestroying = mDestroyingServices.contains(r); serviceDoneExecutingLocked(r, inDestroying, inDestroying); throw e; } catch (RemoteException e) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r); // Keep the executeNesting count accurate. final boolean inDestroying = mDestroyingServices.contains(r); serviceDoneExecutingLocked(r, inDestroying, inDestroying); return false; } } return true; }Copy the code

We see a lot of state Richard armitage pp. Thread. ScheduleBindService, previous analysis app start or not is call scheduleCreateService, we call scheduleBindService binding service here is, So we also go to the ActivityThread and scheduleBindService method. The process is the same as when we created the service. We go directly to 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

Enter the handleBindService method

    private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if(s ! = null) { try { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(); try {if(! data.rebind) { IBinder binder = s.onBind(data.intent); ActivityManagerNative.getDefault().publishService( data.token, data.intent, binder); }else {
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                }
            } 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

IBinder binder = s.bind (data.intent); IBinder binder = s.bind (data.intent); This line of code returns an IBinder object, which is the onBind method of LeoAidlService.

   public void publishService(IBinder token, Intent intent, IBinder service) {
        // Refuse possible leaked file descriptors
        if(intent ! = null && intent.hasFileDescriptors() ==true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        synchronized(this) {
            if(! (token instanceof ServiceRecord)) { throw new IllegalArgumentException("Invalid service token"); } mServices.publishServiceLocked((ServiceRecord)token, intent, service); }}Copy the code

Then we go to publishServiceLocked and we get to the ActiveServices#publishServiceLocked method


    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
                    + "" + intent + ":" + service);
            if(r ! = null) { Intent.FilterComparison filter = new Intent.FilterComparison(intent); IntentBindRecord b = r.bindings.get(filter);if(b ! = null && ! b.received) { b.binder = service; b.requested =true;
                    b.received = true;
                    for (int conni=r.connections.size()-1; conni>=0; conni--) {
                        ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                        for (int i=0; i<clist.size(); i++) {
                            ConnectionRecord c = clist.get(i);
                            if(! filter.equals(c.binding.intent.intent)) {if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Not publishing to: " + c);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
                                if (DEBUG_SERVICE) Slog.v(
                                        TAG_SERVICE, "Published intent: " + intent);
                                continue;
                            }
                            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
                            try {
                                c.conn.connected(r.name, service);
                            } catch (Exception e) {
                                Slog.w(TAG, "Failure sending service " + r.name +
                                      " to connection " + c.conn.asBinder() +
                                      " (in " + c.binding.client.processName + ")", e);
                            }
                        }
                    }
                }

                serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false); } } finally { Binder.restoreCallingIdentity(origId); }}Copy the code

Connected (r.name, service); This will eventually call the onServiceConnected (or onServiceDisconnected) code on the client side and return the IBinder object to the client.

 public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if(map ! = null) { sd = map.get(c); }if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (map == null) {
                    map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            returnsd.getIServiceConnection(); }}Copy the code

Return sd.getiServiceconnection (); So let’s go to the getIServiceConnection method,

        IServiceConnection getIServiceConnection() {
            return mIServiceConnection;
        }
Copy the code

We found that the getIServiceConnection method returns a mIServiceConnection object, so let’s see what the heck is a mIServiceConnection object?


    static final class ServiceDispatcher {
        private final ServiceDispatcher.InnerConnection mIServiceConnection;
Copy the code

To enter the ServiceDispatcher InnerConnection

private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            public void connected(ComponentName name, IBinder service) throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if(sd ! = null) { sd.connected(name, service); }}}Copy the code

Sd. connected(name, service); C. Conn. connected Connected (name, service) is transferred to the sd. Connected (name, service). So let’s go into connected, and finally in the doConnected method

public void doConnected(ComponentName name, IBinder service) {

 // If there was an old service, it is not disconnected.
            if(old ! = null) { mConnection.onServiceDisconnected(name); } // If there is a new service, it is now connected.if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
}
Copy the code

# Here we put the android system AIDL whole mechanism analysis is complete. Finally, to give us an analogy of aiDL from our own example analysis to the system, you have to think about the aiDL process when you look at these system classes, so it’s easy to read the source code.

Android Binder mechanism