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.