Service binding process

Service binding process

Service binding is also a commonly used function in development. In the context environment, a service can be bound by bindService. It can be known by tracing the source code of bindService. Finally, it will be invoked in the parent class ContextWrapper. In this class, the bindService method of mBase is invoked. The implementation of mBase is ContextImpl. Context Creation Part of the source code is as follows (in ContextWrapper):

//Context type, the concrete implementation class is ContextImpl
Context mBase;
  
@Override
public boolean bindService(Intent service, ServiceConnection conn,
        int flags) {
    return mBase.bindService(service, conn, flags);
}

Since the specific implementation of bindService is in the ContextImpl class, let's look at the bindService method of ContextImpl:

@Override
public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
    warnIfCallingFromSystemProcess();
    return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
}

ContextImpl's bindService method calls the bindService Common method again:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
       handler, UserHandle user) {
   ...............
   
   try {
       .............
       
       //Key Code 1
       int res = ActivityManager.getService().bindService(
           mMainThread.getApplicationThread(), getActivityToken(), service,
           service.resolveTypeIfNeeded(getContentResolver()),
           sd, flags, getOpPackageName(), user.getIdentifier());
           
       .............
   } catch (RemoteException e) {
       throw e.rethrowFromSystemServer();
   }
}

At the key code 1, ActivityManager.getService() is the proxy object of AMS. Here, the method of AMS is invoked through cross-process communication. Look at the bindService method of AMS:

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

In the above method, the bindServiceLocked method of ActiveServices is invoked:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
            ......................
            
	        if (s.app != null && b.intent.received) {
	                try {
	                    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 this is the first app connected back to this binding,
	                // and the service had previously asked to be told when
	                // rebound, then do so.
	                if (b.intent.apps.size() == 1 && b.intent.doRebind) {
	                
						//Key Code 2
	                    requestServiceBindingLocked(s, b.intent, callerFg, true);
	                }
	         } else if (!b.intent.requested) {
	         
	      		    //Key Code 3
	                requestServiceBindingLocked(s, b.intent, callerFg, false);
	         }
	      ......................
}

At Key Code 2 and Key Code 3, the requestService BindingLocked method is invoked:

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 (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                + " rebind=" + rebind);
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);

				//Key Code 4
                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;
}

At the four key codes, r.app.thread is the proxy object of ApplicaitonThread, where AMS initiates a cross-process communication request to the client, calling the scheduleBindService method in ApplicaitonThread:

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());
     
     //Key Code 5
     sendMessage(H.BIND_SERVICE, s);
}

At key code 5, a message bound to the service is sent to the handleMessage method in class H:

case BIND_SERVICE:
          Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
          handleBindService((BindServiceData)msg.obj);
          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
          break;

After receiving the binding service message in class H, the handleBindService method of ActivityThread is invoked:

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) {
                    
						//Key code 6
                        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) {
                    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);
                }
            }
        }
}

At the key code 6, we call the onBind method of Service to return the binder object, which is created by ourselves (the implementation class of aidl stub). Then we publish the binder service through the publishService method of AMS. Let's look at the publishService method of AMS:

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

		//Key code 7
        mServices.publishServiceLocked((ServiceRecord)token, intent, service);
    }
}

The publishService Locked method of ActiveServices is invoked at key code 7:

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    final long origId = Binder.clearCallingIdentity();
         try {
			
			    //Key code 8C. conn is of type IServiceConnection
                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);
            }
}

The type of c.conn at key code 8 is IServiceConnection, which is implemented as the inner class InnerConnection of LoadedApk's inner class ServiceDispatcher:

public final class LoadedApk {
	
	  ...................
	  
      static final class ServiceDispatcher {
		 
			 ...................
		 
              private final ServiceDispatcher.InnerConnection mIServiceConnection;
             
              //Key code 9
              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, boolean dead)
		                    throws RemoteException {
		                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
		                if (sd != null) {
		                
							//Key code 10
		                    sd.connected(name, service, dead);
		                }
		            }
             }


	   	//connected Method of Loaded Apk.Service Dispatcher
		 public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityThread != null) {
				//Key code 11
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
				//Key code 12
                doConnected(name, service, dead);
            }
         }
     }
}

In the key code 9, InnerConnection implements IServiceConnection.Stub. IServiceConnection is an aidl interface, so InnerConnection has the ability of cross-process communication. In the key code 8, the call of connection method is actually the call of connection method of InnerConnection, which is the key of connection method. Code 10 calls sd's connection method, SD is of LoadedApk. Service Dispatcher type, and mActivityThread is a handler (its real name is H in ActivityThread) at the key code 11 of sd's connection method. It calls back the program to the main thread through H to execute, because RunConnection is a Runnable, so it will execute. RunConnection's run method, RunConnection source code is as follows:

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) {
				//Key code 13
                doConnected(mName, mService, mDead);
            } else if (mCommand == 1) {
                doDeath(mName, mService);
            }
        }

        final ComponentName mName;
        final IBinder mService;
        final int mCommand;
        final boolean mDead;
}

According to the key code 12 and 13, the final program will be executed into the doConnected method of Service Dispatcher. The doConnected source code is as follows:

public void doConnected(ComponentName name, IBinder service, boolean dead) {
       .......................
        // 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 viable service, it is now connected.
        if (service != null) {
		
			//Key code 14
            mConnection.onServiceConnected(name, service);
        } else {
            // The binding machinery worked, but the remote returned null from onBind().
            mConnection.onNullBinding(name);
        }
}

From the key code 14, we can see that the onService connected method of mConnection is invoked, and the type of mConnection is Service Connection. In fact, this mConnection object is the ServiceConnection object created by ourselves in our binderService method. Because this object can not communicate across processes, it is needed. When we communicate with AMS in cross-process, we use an InnerConnection object corresponding to it. The client holds both objects at the same time. So we use InnerConnection when we need to cross-process. We initiate cross-process communication to AMS when the client binds the service. When AMS is finished, we call back InnerConnectio. The connected method of N calls the onService connected method of the Service Connection object passed in by the client in the connected method, so that the binding of the service is completed.

Added by Lautarox on Mon, 09 Sep 2019 10:23:16 +0300