Android AOSP 6.0.1 startService startup process analysis

Android AOSP 6.0.1 startService startup process analysis

The previous section traced the startActivity startup process, this section continues to trace the startService startup process.

The above is the Service life cycle that Android developers are very familiar with. Today we mainly analyze how startService() works in Framework.

The following is the source code of the Service to be started.

package com.demo.framework import android.app.Service import android.content.Intent import android.os.IBinder import android.util.Log class DemoService : Service () { companion object { const val TAG = "DemoService" } override fun onBind (intent: Intent ) : IBinder? { Log.d(TAG, "onBind" ) return null } override fun onCreate () { Log.d(TAG, "onCreate" ) super .onCreate() } override fun onStartCommand (intent: Intent ?, flags: Int , startId: Int ) : Int { Log.d(TAG, "onStartCommand flags = $flags ,startId = $startId " ) return super .onStartCommand(intent, flags, startId) } override fun onDestroy () { Log.d(TAG, "onDestroy" ) super .onDestroy() } } Copy code

Let's call startService() to start it.

startService (the Intent ( the this , DemoService :: class .java)) Copy the code

Log has been printed and the service started successfully.

07-07 20:54:25.829 4964-4964/com.demo.framework D/DemoService: onCreate 07-07 20:54:25.829 4964-4964/com.demo.framework D/DemoService: onStartCommand flags=0,startId=1 Copy code

We know that since there is no separate configuration for Service to run in another process, it runs in the same process as Demo App Activity.

Calling the startService method in the Activity actually calls the startService method of the ContextWrapper.

ContextWrapper is a proxy implementation of Context, which just delegates all its calls to another Context. Subclassing to modify behavior without changing the original Context. mBase is a Context type. In the ContextWrapper class, mBase is set by the following constructor or by calling the attachBaseContext method.

frameworks/base/core/java/android/content/ContextWrapper.java

public class ContextWrapper extends Context { Context mBase; public ContextWrapper (Context base) { mBase = base; } protected void attachBaseContext (Context base) { if (mBase != null ) { throw new IllegalStateException( "Base context already set" ); } mBase = base; } ...... @Override public ComponentName startService (Intent service) { return mBase.startService(service); } ...... } Copy code

First of all, we must find out which subclass of mBase is specifically? Let's take a look at the inheritance relationship of Activity. Activity inherits from ContextThemeWrapper, this class seems to be related to ContextWrapper.

frameworks/base/core/java/android/app/Activity.java

public class the Activity the extends ContextThemeWrapper the implements LayoutInflater . FACTORY2 , the Window . the Callback , KeyEvent . the Callback , OnCreateContextMenuListener , ComponentCallbacks2 , the Window . OnWindowDismissedCallback duplicated code

Enter ContextThemeWrapper, and sure enough, ContextThemeWrapper inherits from ContextWrapper. Now it is very clear. 1. pass the Context in the Activity constructor, and call the super method to assign a value to mBase; second, pass the Context by calling the attachBaseContext method. Looking along the line of thought, I couldn't find the Activity constructor at all, indicating that the system wants to initialize Activity by calling its default constructor Activity(), so you can only set the Context according to the second idea.

frameworks/base/core/java/android/view/ContextThemeWrapper.java

public class ContextThemeWrapper extends ContextWrapper { private int mThemeResource; private Resources.Theme mTheme; private LayoutInflater mInflater; private Configuration mOverrideConfiguration; private Resources mResources; public ContextThemeWrapper () { super ( null ); } public ContextThemeWrapper (Context base, @StyleRes int themeResId) { super (base); mThemeResource = themeResId; } public ContextThemeWrapper (Context base, Resources.Theme theme) { super (base); mTheme = theme; } @Override protected void attachBaseContext (Context newBase) { super .attachBaseContext(newBase); } ...... } Copy code

Find the attachBaseContext method in the Activity source code, and locate it quickly.

frameworks/base/core/java/android/app/Activity.java

public class Activity extends ContextThemeWrapper implements LayoutInflater . Factory2 , Window . Callback , KeyEvent . Callback , OnCreateContextMenuListener , ComponentCallbacks2 , Window . OnWindowDismissedCallback { ...... final void attach (Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor) { attachBaseContext(context); ...... } ...... } Copy code

Next is a new problem. Where is the attach method in Activity called? Intuitively it should be called in the Instrumentation class. It was found after searching the corresponding attach method.

frameworks/base/core/java/android/app/Instrumentation.java

public class Instrumentation { ...... public Activity newActivity (Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException { Activity activity = (Activity)clazz.newInstance(); ActivityThread aThread = null ; activity.attach(context, aThread, this , token, 0 , application, intent, info, title, parent, id, (Activity.NonConfigurationInstances)lastNonConfigurationInstance, new Configuration(), null , null ); return activity; } public Activity newActivity (ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return (Activity)cl.loadClass(className).newInstance(); } ...... } Copy code

In one of the methods to create an Activity, the context is still a formal parameter passed in from an external call; the other function does not pass in the Context, indicating that after the Activity object is created, its attach method needs to be called to pass the Context by itself. At this time, it is not difficult to guess that the newActivity call occurs in the ActivityThread class. After searching for the newActivity method, it is found that it is called by the performLaunchActivity function.

1. Create an Activity object

2. Call the attach method of the Activity object

It is found that the Context object is returned by calling the createBaseContextForActivity method.

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... private Activity performLaunchActivity (ActivityClientRecord r, Intent customIntent) { ...... Activity activity = null ; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); //1. Create an Activity object activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null ) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { Application app = r.packageInfo.makeApplication( false , mInstrumentation); if (localLOGV) Slog.v(TAG, "Performing launch of " + r); if (localLOGV) Slog.v( TAG, r + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + r.packageInfo.getPackageName() + ", comp=" + r.intent.getComponent().toShortString() + ", dir=" + r.packageInfo.getAppDir()); if (activity != null ) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); //2. Call the attach method activity of the Activity object . attach(appContext, this , getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor); if (customIntent != null ) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null ; activity.mStartedActivity = false ; int theme = r.activityInfo.getThemeResource(); if (theme != 0 ) { activity.setTheme(theme); } activity.mCalled = false ; if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } ...... } r.paused = true ; mActivities.put(r.token, r); } catch (SuperNotCalledException e) { throw e; } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to start activity " + component + ": " + e.toString(), e); } } return activity; } private Context createBaseContextForActivity (ActivityClientRecord r, final Activity activity) { int displayId = Display.DEFAULT_DISPLAY; try { displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token); } catch (RemoteException e) { } ContextImpl appContext = ContextImpl.createActivityContext( this , r.packageInfo, displayId, r.overrideConfig); appContext.setOuterContext(activity); Context baseContext = appContext; ...... return baseContext; } ...... } Copy code

The createBaseContextForActivity function continues to call the static method createActivityContext of ContextImpl to create ContextImpl, and finally returns it.

The role of the ContextImpl class: a general implementation of the Context API, which provides a basic context object for Activity and other application components.

Back to the main line, the startService we call in ContextWrapper actually ultimately uses the ContextImpl object, calling its startService method, which further calls the startServiceCommon function. At this point, you are very familiar. First get the ActivityManagerProxy object, and then finally call the startService method in the remote ActivityManagerService through the Binder mechanism.

frameworks/base/core/java/android/app/ContextImpl.java

class ContextImpl extends Context { ...... @Override public ComponentName startService (Intent service) { warnIfCallingFromSystemProcess(); return startServiceCommon(service, mUser); } ...... private ComponentName startServiceCommon (Intent service, UserHandle user) { try { validateServiceIntent(service); service.prepareToLeaveProcess(); ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), 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()); } } return cn; } catch (RemoteException e) { throw new RuntimeException( "Failure from system" , e); } } ...... static ContextImpl createActivityContext (ActivityThread mainThread, LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) { if (packageInfo == null ) throw new IllegalArgumentException( "packageInfo" ); return new ContextImpl( null , mainThread, packageInfo, null , null , false , null , overrideConfiguration, displayId); } ...... } Copy code

The above analysis is completely done through a logical method. In fact, there is no such trouble at all. After nailing a log, you can immediately know what kind of monster it is. I added the following log print to the Activity attach method.

Log.d( "lhw" , "context=" + context); Copy code

The following is the result captured in Logcat, further confirming that the above reasoning is completely correct.

07-08 01:32:45.469 2562-2562/? D/lhw: context=android.app.ContextImpl@19c52a8

Continue to look at the startService method in ActivityManagerProxy.

frameworks/base/core/java/android/app/ActivityManagerNative.java

class ActivityManagerProxy implements IActivityManager { ...... public ComponentName startService (IApplicationThread caller, Intent service, String resolvedType, 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 ); service.writeToParcel(data, 0 ); data.writeString(resolvedType); data.writeString(callingPackage); data.writeInt(userId); mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0 ); reply.readException(); ComponentName res = ComponentName.readFromParcel(reply); data.recycle(); reply.recycle(); return res; } ...... } Copy code

Immediately transfer to the ActivityManagerService startService method.

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public final class ActivityManagerService extends ActivityManagerNative implements Watchdog . Monitor , BatteryStatsImpl . BatteryCallback { ...... @Override public ComponentName startService (IApplicationThread caller, Intent service, String resolvedType, String callingPackage, int userId) throws TransactionTooLargeException { enforceNotIsolatedCaller( "startService" ); //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" ); } if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "startService: " + service + " type=" + resolvedType); synchronized ( this ) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); ComponentName res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, callingPackage, userId); Binder.restoreCallingIdentity(origId); return res; } } ...... } Copy code

mServices is an ActiveServices object. So it is actually calling the startServiceLocked method of the ActiveServices object.

Here is the call chain:

startServiceLocked -> startServiceInnerLocked -> bringUpServiceLocked -> realStartServiceLocked

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

public final class ActiveServices { ...... ComponentName startServiceLocked (IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, String callingPackage, int userId) throws TransactionTooLargeException { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service + "type=" + resolvedType + " args=" + service.getExtras()); final boolean callerFg; if (caller != null ) { final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller); if (callerApp == null ) { throw new SecurityException( "Unable to find app for caller " + caller + "(pid=" + Binder.getCallingPid() + ") when starting service " + service); } callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE; } else { callerFg = true ; } ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, callingPackage, callingPid, callingUid, userId, true , callerFg); if (res == null ) { return null ; } if (res.record == null ) { return new ComponentName( "!" , res.permission != null ? res.permission: "private to package" ); } ServiceRecord r = res.record; if (!mAm.getUserManagerLocked().exists(r.userId)) { Slog.d(TAG, "Trying to start service with non-existent user! " + r.userId); return null ; } NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked( callingUid, r.packageName, service, service.getFlags(), null , r.userId); if (unscheduleServiceRestartLocked(r, callingUid, false )) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r); } r.lastActivity = SystemClock.uptimeMillis(); r.startRequested = true ; r.delayedStop = false ; r.pendingStarts.add( new ServiceRecord.StartItem(r, false , r.makeNextStartId(), service, neededGrants)); final ServiceMap smap = getServiceMap(r.userId); boolean addToStarting = false ; if (!callerFg && r.app == null && mAm.mStartedUsers.get(r.userId) != null ) { ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false ); if (proc == null || proc.curProcState> ActivityManager.PROCESS_STATE_RECEIVER) { //If this is not coming from a foreground caller, then we may want //to delay the start if there are already other background services //that are starting. This is to avoid process start spam when lots //of applications are all handling things like connectivity broadcasts. //We only do this for cached processes, because otherwise an application //can have assumptions about calling startService() for a service to run //in its own process, and for that process to not be killed before the //service is started. This is especially the case for receivers, which //may start a service in onReceive() to do some additional work and have //initialized some global state as part of that. if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of " + r + " in " + proc); if (r.delayed) { //This service is already scheduled for a delayed start; just leave //it still waiting. if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r); return r.name; } if (smap.mStartingBackground.size() >= mMaxStartingBackground) { //Something else is starting, delay! Slog.i(TAG_SERVICE, "Delaying start of: " + r); smap.mDelayedStartList.add(r); r.delayed = true ; return r.name; } if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r); addToStarting = true ; } else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) { //We slightly loosen when we will enqueue this new service as a background //starting service we are waiting for, to also include processes that are //currently running other services or receivers. addToStarting = true ; if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying, but counting as bg: " + r); } else if (DEBUG_DELAYED_STARTS) { StringBuilder sb = new StringBuilder( 128 ); sb.append( "Not potential delay (state=" ).append(proc.curProcState) .append( '' ).append(proc.adjType); String reason = proc.makeAdjReason(); if (reason != null ) { sb.append( '' ); sb.append(reason); } sb.append( "): " ); sb.append(r.toString()); Slog.v(TAG_SERVICE, sb.toString()); } } else if (DEBUG_DELAYED_STARTS) { if (callerFg) { Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + "uid=" + callingUid + " pid=" + callingPid + "): " + r); } else if (r.app != null ) { Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r); } else { Slog.v(TAG_SERVICE, "Not potential delay (user " + r.userId + " not started): " + r); } } return startServiceInnerLocked(smap, service, r, callerFg, addToStarting); } ComponentName startServiceInnerLocked (ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { ProcessStats.ServiceState stracker = r.getTracker(); if (stracker != null ) { stracker.setStarted( true , mAm.mProcessStats.getMemFactorLocked(), r.lastActivity); } r.callStart = false ; synchronized (r.stats.getBatteryStats()) { r.stats.startRunningLocked(); } String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false ); if (error != null ) { return new ComponentName( "!!" , error); } if (r.startRequested && addToStarting) { boolean first = smap.mStartingBackground.size() == 0 ; smap.mStartingBackground.add(r); r.startingBgTimeout = SystemClock.uptimeMillis() + BG_START_TIMEOUT; if (DEBUG_DELAYED_SERVICE) { RuntimeException here = new RuntimeException( "here" ); here.fillInStackTrace(); Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r, here); } else if (DEBUG_DELAYED_STARTS) { Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r); } if (first) { smap.rescheduleDelayedStarts(); } } else if (callerFg) { smap.ensureNotStartingBackground(r); } return r.name; } ...... private final String bringUpServiceLocked (ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting) throws TransactionTooLargeException { //Slog.i(TAG, "Bring up service:"); //r.dump(" "); if (r.app != null && r.app.thread != null ) { sendServiceArgsLocked(r, execInFg, false ); return null ; } if (!whileRestarting && r.restartDelay> 0 ) { //If waiting for a restart, then do nothing. return null ; } if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent); //We are now bringing the service up, so no longer in the //restarting state. if (mRestartingServices.remove(r)) { r.resetRestartCounter(); clearRestartingIfNeededLocked(r); } //Make sure this service is no longer considered delayed, we are starting it now. if (r.delayed) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r); getServiceMap(r.userId).mDelayedStartList.remove(r); r.delayed = false ; } //Make sure that the user who owns this service is started. If not, //we don't want to allow it to run. if (mAm.mStartedUsers.get(r.userId) == null ) { String msg = "Unable to launch app " + r.appInfo.packageName + "/" + r.appInfo.uid + " for service " + r.intent.getIntent() + ": user" + r.userId + "is stopped" ; Slog.w(TAG, msg); bringDownServiceLocked(r); return msg; } //Service is now being launched, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( r.packageName, false , r.userId); } catch (RemoteException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " + r.packageName + ":" + e); } final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0 ; final String procName = r.processName; 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); 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 //we call startProcessLocked() we will get a new isolated //process, starting another process if we are currently waiting //for a previous process to come up. To deal with this, we store //in the service any current isolated process it is running in or //waiting to have come up. app = r.isolatedProc; } //Not running - get it started, and enqueue this service record //to be executed when the app comes up. 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; } } if (!mPendingServices.contains(r)) { mPendingServices.add(r); } if (r.delayedStop) { //Oh and hey we've already been asked to stop! r.delayedStop = false ; if (r.startRequested) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Applying delayed stop (in bring up): " + r); stopServiceLocked(r); } } return null ; } ...... private final void realStartServiceLocked (ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { if (app.thread == null ) { throw new RemoteException(); } if (DEBUG_MU) Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid + ", ProcessRecord.uid = " + app.uid); r.app = app; r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); final boolean newService = app.services.add(r); bumpServiceExecutingLocked(r, execInFg, "create" ); mAm.updateLruProcessLocked(app, false , null ); mAm.updateOomAdjLocked(); boolean created = false ; try { if (LOG_SERVICE_START_STOP) { String nameTerm; int lastPeriod = r.shortName.lastIndexOf( '.' ); nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod): r.shortName; EventLogTags.writeAmCreateService( r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid); } synchronized (r.stats.getBatteryStats()) { r.stats.startLaunchedLocked(); } mAm.ensurePackageDexOpt(r.serviceInfo.packageName); 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) { Slog.w(TAG, "Application dead when creating service " + r); mAm.appDiedLocked(app); throw e; } finally { if (!created) { //Keep the executeNesting count accurate. final boolean inDestroying = mDestroyingServices.contains(r); serviceDoneExecutingLocked(r, inDestroying, inDestroying); //Cleanup. if (newService) { app.services.remove(r); r.app = null ; } //Retry. if (!inDestroying) { scheduleServiceRestartLocked(r, false ); } } } requestServiceBindingsLocked(r, execInFg); updateServiceClientActivitiesLocked(app, null , true ); //If the service is in the started state, and there are no //pending arguments, then fake up one so its onStartCommand() will //be called. if (r.startRequested && r.callStart && r.pendingStarts.size () == 0 ) { r.pendingStarts.add( new ServiceRecord.StartItem(r, false , r.makeNextStartId(), null , null )); } sendServiceArgsLocked(r, execInFg, true ); if (r.delayed) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r); getServiceMap(r.userId).mDelayedStartList.remove(r); r.delayed = false ; } if (r.delayedStop) { //Oh and hey we've already been asked to stop! r.delayedStop = false ; if (r.startRequested) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Applying delayed stop (from start): " + r); stopServiceLocked(r); } } } ...... } Copy code

In the realStartServiceLocked function, the scheduleCreateService method of the member variable thread (IApplicationThread) of the ProcessRecord object is called. In fact, the ApplicationThreadProxy object is obtained to call the scheduleCreateService method in the remote ApplicationThread.

frameworks/base/core/java/android/app/ApplicationThreadNative.java

class ApplicationThreadProxy implements IApplicationThread { ...... public final void scheduleCreateService (IBinder token, ServiceInfo info, CompatibilityInfo compatInfo, int processState) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeStrongBinder(token); info.writeToParcel(data, 0 ); compatInfo.writeToParcel(data, 0 ); data.writeInt(processState); try { mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null , IBinder.FLAG_ONEWAY); } catch (TransactionTooLargeException e) { Log.e( " CREATE_SERVICE " , "Binder failure starting service; service=" + info); throw e; } data.recycle(); } ...... } Copy code

Let's look at the scheduleCreateService method in ApplicationThread.

frameworks/base/core/java/android/app/ActivityThread.java

private class ApplicationThread extends ApplicationThreadNative { ...... 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 code

Similar to the startActivity section, the scheduleCreateService method calls the sendMessage method in ActivityThread (ApplicationThread is an internal class).

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... private void sendMessage ( int what, Object obj) { sendMessage(what, obj, 0 , 0 , false ); } ...... private void sendMessage ( int what, Object obj, int arg1, int arg2, boolean async) { if (DEBUG_MESSAGES) Slog.v( TAG, "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + "/" + obj); Message msg = Message.obtain(); msg.what = what; msg.obj = obj; msg.arg1 = arg1; msg.arg2 = arg2; if (async) { msg.setAsynchronous( true ); } mH.sendMessage(msg); } ...... } Copy code

mH is an H type. H (ActivityThread internal class) inherits from Handler and will eventually call its handleMessage method.

frameworks/base/core/java/android/app/ActivityThread.java

private class H extends Handler { ...... public void handleMessage (Message msg) { switch (msg.what) { ...... case CREATE_SERVICE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate" ); handleCreateService((CreateServiceData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break ; ...... } } ...... } Copy code

In the handleMessage method of H, enter the CREATE_SERVICE case, and then call the handleCreateService of the ActivityThread object.

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread { ...... 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(); //Create Service object 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 ContextImpl object ContextImpl context = ContextImpl.createAppContext( this , packageInfo); context.setOuterContext(service); Application app = packageInfo.makeApplication( false , mInstrumentation); //Call the attach method of Service service.attach(context, this , data.info.name, data.token, app, ActivityManagerNative.getDefault()); //Call the onCreate method of Service 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 code

Service is an abstract class, we generally override its onCreate method. When I got here, I finally called the onCreate method of DemoService.

frameworks/base/core/java/android/app/Service.java

public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { ...... /** * Called by the system when the service is first created. Do not call this method directly. */ public void onCreate () { } ...... } Copy code

Then follow the above analysis logic to append the print log for verification:

07-08 04:15:45.763 2437-2437/com.demo.framework V/lhw: ContextImpl startService intent=Intent {cmp=com.demo.framework/.DemoService} 07-08 04:15:45.763 2437-2437/com.demo.framework V/lhw: ContextImpl startServiceCommon intent=Intent {cmp=com.demo.framework/.DemoService },user=UserHandle{0} 07-08 04:15:45.764 2437-2437/com.demo.framework V/lhw: ActivityManagerProxy startService 07-08 04:15:45.764 773-1755/system_process V/lhw: ActivityManagerService startService: Intent {cmp=com.demo.framework/.DemoService} type=null 07-08 04:15:45.764 773-1755/system_process V/lhw: ActiveServices startServiceLocked 07-08 04:15:45.764 773-1755/system_process V/lhw: ActiveServices startServiceInnerLocked 07-08 04:15:45.765 773-1755/system_process V/lhw: ActiveServices bringUpServiceLocked 07-08 04:15:45.765 773-1755/system_process V/lhw: ActiveServices bringUpServiceLocked realStartServiceLocked 07-08 04:15:45.765 773-1755/system_process V/lhw: ActiveServices realStartServiceLocked 07-08 04:15:45.765 773-1755/system_process V/lhw: ApplicationThreadProxy scheduleCreateService 07-08 04:15:45.765 2437-2465/com.demo.framework I/lhw: ActivityThread sendMessage SCHEDULE 114 114: 0/CreateServiceData{token=android.os.BinderProxy@710ca5f className=com.demo.framework.DemoService packageName =com.demo.framework intent=null} 07-08 04:15:45.765 2437-2465/com.demo.framework I/lhw: ActivityThread sendMessage SCHEDULE 115 115: 0/ServiceArgsData{token=android.os.BinderProxy@710ca5f startId=1 args=Intent {cmp=com .demo.framework/.DemoService }} 07-08 04:15:45.771 2437-2437/com.demo.framework I/lhw: H handleMessage handling: 114 07-08 04:15:45.771 2437-2437/com.demo.framework I/lhw: H handleMessage CREATE_SERVICE case 07-08 04:15:45.771 2437-2437/com.demo.framework I/lhw: ActivityThread handleCreateService 07-08 04:15:45.775 2437-2437/com.demo.framework D/DemoService: onCreate 07-08 04:15:45.775 2437-2437/com.demo.framework V/lhw: Service onCreate Copy code

Finally, draw a timing diagram as a summary.