ContextImpl to AMS

In addition to calling the startService method, you can also call the bindService method. Let’s take a look at the overall process of calling the bindService method.

Sequence diagram:

BindService with ContextImpl is called when bindService is called

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

Next, look at the bindServiceCommon method

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user){...if(mPackageInfo ! =null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, 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(this);
            int res = ActivityManager.getService().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) {
            throwe.rethrowFromSystemServer(); }}Copy the code

LoadedApk mPackageInfo is used to call getServiceDispatcher to wrap ServiceConnection into IServiceConnection. IServiceConnection is a Binder class that supports boast processes. The AMS bindService method is then called.

Service binding process

Sequence diagram:

Finally, AMS’s bindService method is called. Let’s look at the code

    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) {
            returnmServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId); }}Copy the code

This calls the bindServiceLocked method of mServices of ActiveServices type

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
        If the process is null, throw an exception
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        if (callerApp == null) {
            throw new SecurityException(
                    "Unable to find app for caller " + caller
                    + " (pid=" + Binder.getCallingPid()
                    + ") when binding service " + service);
        }
        // Get information about the Activity bound to the Service
        ActivityRecord activity = null;
        if(token ! =null) {
            activity = ActivityRecord.isInStackLocked(token);
            if (activity == null) {
                Slog.w(TAG, "Binding with unknown activity: " + token);
                return 0; }}...// Get ServiceRecord. ServiceRecord describes a ServiceServiceRecord s = res.record; .try{...// Call retrieveAppBindingLocked of ServiceRecord to retrieve the AppBindRecord
            //AppBindRecord is used to maintain the connection between the Service and the application process when the Service is bound with an Intent
            //. The -processRecord binding Service is stored internally.
            IntentBindRecord = IntentBindRecord = IntentBindRecord = IntentBindRecord = IntentBindRecord = IntentBindRecord
            // All bound communications -arraySet 
      
            AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
            // Generate a ConnectionRecord and place it in ArrayList
      
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);

            IBinder binder = connection.asBinder();
            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
            if (clist == null) {
                clist = newArrayList<ConnectionRecord>(); s.connections.put(binder, clist); } clist.add(c); b.connections.add(c); .if((flags&Context.BIND_AUTO_CREATE) ! =0) {
                s.lastActivity = SystemClock.uptimeMillis();
                // Call the bringUpServiceLocked method, which calls realStartServiceLocked
                // method to start a Service
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) ! =null) {
                    return 0; }}...// if s.pp is not null, the Service has started. S.pp is the ProcessRecord object
            Received indicates that the current application received the Binder returned when the Service was bound.
            // Applications can obtain the access interface to the bound Sercice through Binder
            if(s.app ! =null && b.intent.received) {
            // The onBind method has already been executed. And prepare for a successful binding callback.
                try {
                    / / here Arthur c. onn IServiceConnection, concrete implementation is ServiceDispacher InnerConnection
                    //ServiceDispacher is an inner class of LoadedApk. Connected finally calls H's post method
                    // Send a message to the main thread
                    c.conn.connected(s.name, b.intent.binder, false);
                } catch (Exception e) {
                    Slog.w(TAG, "Failure sending service " + s.shortName
                            + " to connection " + c.conn.asBinder()
                            + " (in " + c.binding.client.processName + ")", e);
                }

                // If the current application process is the first to bind to the Service and the Service has called the onUnbind method
                / / will call requestServiceBindingLocked method.
                if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                    // Passing true here will eventually call back the Service's reBind method. ServiceConnection is not called back
                    / / callback
                    requestServiceBindingLocked(s, b.intent, callerFg, true); }}else if(! b.intent.requested) {/ / if the Service is not binding, will call requestServiceBindingLocked method
                // This is the same method as above, except that the last arguments are true and false
                // This parameter indicates whether to rebind
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }

            getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);

        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        return 1;
    }
Copy the code

Take a look at requestServiceBindingLocked method

    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");
                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

If the condition is met, the scheduleBindService method of the IApplicationThread is called. If the condition is met, the scheduleBindService method of the IApplicationThread is called

        public final void scheduleBindService(IBinder token, Intent intent, boolean rebind, int processState) {
            updateProcessState(processState, false);
            BindServiceData s = new BindServiceData();
            s.token = token;
            s.intent = intent;
            s.rebind = rebind;

            if (DEBUG_SERVICE)
                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
            sendMessage(H.BIND_SERVICE, s);
        }
Copy the code

Again, encapsulate the Service information as BindServiceData and send THE BIND_SERVICE information to H.

                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

The handleBindService method is called

    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); ActivityManager.getService().publishService( data.token, data.intent, binder); }else {
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throwex.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

If data.rebind is false, the Service’s onBind method will be called. If true, the Service’s onRebind method will be called.

Let’s look at the unbound case, where AMS’s publishService method is called. So we’re back to AMS.

Sequence diagram:

Take a look at the AMS publishService method first

    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(! (tokeninstanceof ServiceRecord)) {
                throw new IllegalArgumentException("Invalid service token"); } mServices.publishServiceLocked((ServiceRecord)token, intent, service); }}Copy the code

Method calls the publishServiceLocked method of mServices of ActiveServices type

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        final long origId = Binder.clearCallingIdentity();
        try{...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); .try {
                                c.conn.connected(r.name, service, false);
                            } 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

Methods of Arthur c. onn is IServiceConnection, specific implementation is concrete implementation is ServiceDispacher InnerConnection, ServiceDispacher is LoadedApk inner classes

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

The Connected method of ServiceDispatcher is called

        public void connected(ComponentName name, IBinder service, boolean dead) {
            if(mActivityThread ! =null) {
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else{ doConnected(name, service, dead); }}Copy the code

This calls the POST method of the mActivityThread, which is the Handler, actually H, and runs the RunConnection object on the main thread through the POST method. RunConnection is also an inner class of LoadedApk.

        private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
                mName = name;
                mService = service;
                mCommand = command;
                mDead = dead;
            }

            public void run() {
                if (mCommand == 0) {
                    doConnected(mName, mService, mDead);
                } else if (mCommand == 1) {
                    doDeath(mName, mService);
                }
            }

            final ComponentName mName;
            final IBinder mService;
            final int mCommand;
            final boolean mDead;
        }
Copy the code

Because the command passed in is 0, the doConnected method is executed

        public void doConnected(ComponentName name, IBinder service, boolean dead){ ServiceDispatcher.ConnectionInfo old; ServiceDispatcher.ConnectionInfo info; .// If there was an old service, it is now disconnected.
            if(old ! =null) {
                mConnection.onServiceDisconnected(name);
            }
            if (dead) {
                mConnection.onBindingDied(name);
            }
            // If there is a new service, it is now connected.
            if(service ! =null) { mConnection.onServiceConnected(name, service); }}Copy the code

If it is a newly bound Service, the onServiceConnected callback of ServiceConnection is called. This is the end of the binding process.