above
- Android System Revealed (1) -Activity startup process (1)
- Android System Revealed (1) -Activity startup process (1)
preface
We know that there are two ways to use a Service: start and bind. This article explains the two ways respectively.
The start of the Service
The process for starting a Service is relatively simple and does not change much between releases. The code in this article is captured in 8.1, but it is also applicable in 8-11
ContextImpl call to AMS
We call startService to call ContextWrapper’s startService method
frameworks/base/core/java/android/content/ContextWrapper.java
Context mBase;
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
Copy the code
The implementation of Context is ContextImpl
frameworks/base/core/java/android/app/ContextImpl.java
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
Copy the code
private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) { try { validateServiceIntent(service); service.prepareToLeaveProcess(this); ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), requireForeground, getOpPackageName(), user.getIdentifier()); if (cn ! = null) { if (cn.getPackageName().equals("!" )) { throw new SecurityException( "Not allowed to start service " + service + " without permission " + cn.getClassName()); } else if (cn.getPackageName().equals("!!" )) { throw new SecurityException( "Unable to start service " + service + ": " + cn.getClassName()); } else if (cn.getPackageName().equals("?" )) { throw new IllegalStateException( "Not allowed to start service " + service + ": " + cn.getClassName()); } } return cn; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }}Copy the code
According to reveal the Android system (a) – (on) the Activity start process section. We already know ActivityManager getService () in fact, the AMS
AMS inform ActivityThread
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final ActiveServices mServices; @Override public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, boolean requireForeground, String callingPackage, int userId) throws TransactionTooLargeException { ... synchronized(this) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); ComponentName res; try { res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, requireForeground, callingPackage, userId); } finally { Binder.restoreCallingIdentity(origId); } return res; }}Copy the code
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
throws TransactionTooLargeException {
...
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
}
Copy the code
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { ... String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false); if (error ! = null) { return new ComponentName("!!" , error); }... return r.name; }Copy the code
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, Boolean permissionsReviewRequired) throws TransactionTooLargeException if {/ / send the Service parameters (of state Richard armitage pp! = null && r.app.thread ! = null) { sendServiceArgsLocked(r, execInFg, false); return null; }... final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) ! = 0; final String procName = r.processName; String hostingType = "service"; ProcessRecord app; if (! isolated) { app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false); if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); if (app ! = null && app.thread ! = null) { try { app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats); // StartService 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. } } else { // If this service runs in an isolated process, then each time ... } // Create if (app == null &&! permissionsReviewRequired) { if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, hostingType, 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; }}... return null; }Copy the code
The bringUpServiceLocked method gets the process of the Service and starts it with realStartServiceLocked if it exists. Otherwise, create the process and start the Service
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ... boolean created = false; try { ... app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE); app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState); r.postNotification(); created = true; } catch (DeadObjectException e) { ... } finally { ... }... }Copy the code
App. thread refers to IApplicationThread, which is implemented as the inner class ApplicationThread of ActivityThread. ApplicationThread inherits from iApplicationThread.stub
ActivityThread start the Service
frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread
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
The CREATE_SERVICE command is then sent to mH, which is received in H’s handleMessage
frameworks/base/core/java/android/app/ActivityThread.java
public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case CREATE_SERVICE: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj))); handleCreateService((CreateServiceData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; }}Copy the code
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(); / / package information LoadedApk packageInfo = getPackageInfoNoCheck (data. Info. ApplicationInfo, data.com patInfo); Service service = null; Try {/ / for class loader Java. Lang. This 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); / / create a context ContextImpl context. = ContextImpl createAppContext (this, packageInfo); context.setOuterContext(service); / / create or obtain Application Application app. = packageInfo makeApplication (false, mInstrumentation); / / initialize the Service Service. Attach (context, this, the data. The info. Name, data. The token, app, ActivityManager. GetService ()); Service.oncreate (); mServices.put(data.token, service); Try {/ / notify the AMS ActivityManager. GetService () serviceDoneExecuting (data token, SERVICE_DONE_EXECUTING_ANON, 0, 0). } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } catch (Exception e) { if (! mInstrumentation.onException(service, e)) { throw new RuntimeException( "Unable to create service " + data.info.name + ": " + e.toString(), e); }}}Copy the code
This step does the following:
- Obtaining package Information
- Get the class loader
- Creating a Service Instance
- Create context
- Create or get the Application
- Initialize the Service
- The callback notifies the developer that the service has been created
- Notify the AMS
The Service of the binding
ContextImpl call to AMS
We call startService to call ContextWrapper’s startService method
frameworks/base/core/java/android/content/ContextWrapper.java
Context mBase;
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
return mBase.bindService(service, conn, flags);
}
Copy the code
The implementation of Context is ContextImpl
frameworks/base/core/java/android/app/ContextImpl.java
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
Copy the code
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { IServiceConnection sd; . if (mPackageInfo ! = null) { sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); } else { throw new RuntimeException("Not supported in system context"); }... validateServiceIntent(service); try { ... 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); } return res ! = 0; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }}Copy the code
AMS inform ActivityThread
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final ActiveServices mServices; public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException { ... synchronized(this) { return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, callingPackage, userId); }}Copy the code
(Android 10 will skip to the bindIsolatedService method first)
public int bindService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String callingPackage, int userId) throws TransactionTooLargeException { return bindIsolatedService(caller, token, service, resolvedType, connection, flags, null, callingPackage, userId); } public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, String instanceName, String callingPackage, int userId) throws TransactionTooLargeException { ... synchronized(this) { return mServices.bindServiceLocked(caller, token, service, resolvedType, connection, flags, instanceName, callingPackage, userId); }}Copy the code
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, String callingPackage, final int userId) throws TransactionTooLargeException { ... / / record process information final ProcessRecord callerApp = mAm. GetRecordForAppLocked (caller); . // Activity information ActivityRecord Activity = null; if (token ! = null) { activity = ActivityRecord.isInStackLocked(token); if (activity == null) { Slog.w(TAG, "Binding with unknown activity: " + token); return 0; }}... try { ... / / Service and the relationship between the application AppBindRecord b = s.r etrieveAppBindingLocked (Service, callerApp); ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent); IBinder binder = connection.asBinder(); ArrayList<ConnectionRecord> clist = s.connections.get(binder); . // If BIND_AUTO_CREATE is set, the bringUpServiceLocked method is started. = 0) { s.lastActivity = SystemClock.uptimeMillis(); if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) ! = null) { return 0; }}... if (s.app ! = null && b.intent.received) { try { c.conn.connected(s.name, b.intent.binder, false); } catch (Exception e) { ... } if (b.intent.apps.size() == 1 && b.intent.doRebind) { requestServiceBindingLocked(s, b.intent, callerFg, true); } } else if (! b.intent.requested) { requestServiceBindingLocked(s, b.intent, callerFg, false); } getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s); } finally { Binder.restoreCallingIdentity(origId); } return 1; }Copy the code
BringUpServiceLocked (BIND_AUTO_CREATE); bringUpServiceLocked (); bringUpServiceLocked (); bringUpServiceLocked (); Behind the back reference Service startup) call 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) { .. } catch (RemoteException e) { ... } } return true; }Copy the code
App. thread refers to IApplicationThread, which is implemented as the inner class ApplicationThread of ActivityThread. ApplicationThread inherits from iApplicationThread.stub
ActivityThread binding Service
frameworks/base/core/java/android/app/ActivityThread.java#ApplicationThread
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
It then sends the BIND_SERVICE command to the mH, which is received in H’s handleMessage
frameworks/base/core/java/android/app/ActivityThread.java
public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { 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
final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
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) {
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
MService is a Map that stores the relationship between token and Server. It obtains the Service to bind from it and then determines whether the Service is bound for the first time. If so, it calls onBind method and AMS publishService method. Otherwise, call onRebind and call AMS’s serviceDoneExecuting method
Let’s look at the publishService method first
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
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
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) { final long origId = Binder.clearCallingIdentity(); try { ... 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); . try { c.conn.connected(r.name, service, false); } catch (Exception e) { ... }}}}... serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false); } } finally { Binder.restoreCallingIdentity(origId); }}Copy the code
Behind it is go serviceDoneExecutingLocked method, we continue to see Arthur c. onn. Connected
frameworks/base/services/core/java/com/android/server/am/ConnectionRecord.java
final IServiceConnection conn; // The client connection.
Copy the code
IServiceConnection is an AIDL interface, Concrete implementation for ServiceDispatcher InnerConnection, including ServiceDispatcher is LoadedApk inner classes frameworks/base/core/java/android/app/LoadedApk.java
static final class ServiceDispatcher { ... 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) { sd.connected(name, service, dead); }}}}Copy the code
Then look at the ServiceDispatcher connected method of frameworks/base/core/Java/android/app/LoadedApk java# ServiceDispatcher
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 method posts a Runnable to the ActivityThread
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
Inside Runnable is the code for connecting and closing connections.
public void doConnected(ComponentName name, IBinder service, boolean dead) { ServiceDispatcher.ConnectionInfo old; ServiceDispatcher.ConnectionInfo info; synchronized (this) { if (mForgotten) { // We unbound before receiving the connection; ignore // any connection received. return; } old = mActiveConnections.get(name); if (old ! = null && old.binder == service) { // Huh, already have this one. Oh well! return; } if (service ! = null) { // A new service is being connected... set it all up. info = new ConnectionInfo(); info.binder = service; info.deathMonitor = new DeathMonitor(name, service); try { service.linkToDeath(info.deathMonitor, 0); mActiveConnections.put(name, info); } catch (RemoteException e) { // This service was dead before we got it... just // don't do anything with it. mActiveConnections.remove(name); return; } } else { // The named service is being disconnected... clean up. mActiveConnections.remove(name); } if (old ! = null) { old.binder.unlinkToDeath(old.deathMonitor, 0); } } // 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
When binding success callback mConnection. OnServiceConnected (name, service) method
conclusion
The start of the Service
- The Context to inform AMS
- AMS inform ActivityThread
- ActivityThread start the Service
ContextImpl call to AMS
AMS inform ActivityThread
ActivityThread start the Service
The Service of the binding
The binding process is complicated, and it is difficult to describe the simple sequence diagram, so a flow chart is added
- The Context to inform AMS
- AMS inform ActivityThread
- If BIND_AUTO_CREATE is set, the service is directly started; otherwise, the binding continues
- ActivityThread binding Service
- If the binding for the first time, call the AMS publishService method, connection Service, and callback onBind, or direct call AMS and callback onReBind serviceDoneExecutingLocked method
ContextImpl call to AMS
AMS inform ActivityThread
ActivityThread binding Service
reference
[1] LIU Wangshu. Advanced Decryption for Android [M]: Publishing House of Electronics Industry,2018-10