reason
I used to work in a small company, responsible for all the affairs of the upper level. In order to provide development convenience to customers (especially small customers), the company will forcibly remove some restrictions, including the restrictions on starting the Service.
This article analyzes the overall startup process of Service, and also mentions some limitations of Service startup. However, readers must understand how Service is started by themselves, including the startup of the front-end Service. These basic knowledge will not be mentioned too much in this article.
Start Service
The service starts to call the following method
// private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) { try { // From Android 5.0, the Service must be started with an explicit Intent validateServiceIntent(service); (this); // Start Service via AMS ComponentName cn = ().startService( (), service, (getContentResolver()), requireForeground, getOpPackageName(), getAttributionTag(), ()); if (cn != null) { // ... Handle exception results... } // After successful startup, return the started Service component name return cn; } catch (RemoteException e) { throw (); } }
Service is finally started through AMS. Once it is successfully started, it will return the component name of the Service. By returning the result, it can be judged whether the Service is successfully started, which is often ignored by many developers.
AMS will eventually call ActiveService#startServiceLocked() to start Service
ActivityManagerService handes all transactions of the Service to ActiveServices for processing.
// ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage, @Nullable String callingFeatureId, final int userId, boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException { // Is the caller in the foreground? final boolean callerFg; if (caller != null) { final ProcessRecord callerApp = (caller); if (callerApp == null) { throw new SecurityException( "Unable to find app for caller " + caller + " (p) when starting service " + service); } callerFg = () != ProcessList.SCHED_GROUP_BACKGROUND; } else { callerFg = true; } // 1. Query Service // Actually, this is to get ServiceRecord from the cache, or create ServiceRecord and cache ServiceLookupResult res = retrieveServiceLocked(service, null, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, false, false); if (res == null) { return null; } if ( == null) { return new ComponentName("!", != null ? : "private to package"); } // Get Service records from the query results ServiceRecord r = ; // 2. Implement restrictions on Service startup // ... if (forcedStandby || (! && !fgRequired)) { // The process where the Service is located is in the background and cannot start the Service final int allowed = (, , , callingPid, false, false, forcedStandby); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { (TAG, "Background start not allowed: service " + service + " to " + + " from p u pkg=" + callingPackage + " startFg?=" + fgRequired); // ... UidRecord uidRec = (); return new ComponentName("?", "app is in background uid " + uidRec); } } // ... // 3. Go to the next step Service startup process return startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg, allowBackgroundActivityStarts, backgroundActivityStartsToken); }
First, create a record for the Service on the server and cache it. This process is relatively simple and readers can analyze it themselves.
Then, before the Service is started, some restrictions are imposed. The code shows a restriction that the background app cannot start the Service, but the code with other restrictions is omitted. Interested readers can analyze it themselves.
This article aims to analyze the service startup process. For some small details, due to space limitations, I will not analyze them carefully. Please read the code yourself.
Finally, we will enter the next step of starting process
When the service starts, several similar functions will be called, but each function has the most part of the function, so that a huge function can be divided into several small functions, making the code more readable. We need to learn this approach.
private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service, int callingUid, int callingPid, boolean fgRequired, boolean callerFg, boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException { // ... // 1. Update ServiceRecord data = (); = true; = false; = fgRequired; // Save the parameters to be sent to the Service (new (r, false, (), service, neededGrants, callingUid)); // Grant app op permissions to start the front desk service if (fgRequired) { // ... } // The following paragraph determines the value of addToStarting final ServiceMap smap = getServiceMapLocked(); boolean addToStarting = false; // Pay attention to the judgment conditions here // !callerFg means that the caller is not in the foreground // !fgRequired indicates that the yes and non-foreground service is started // == null means that the Service has not been started yet if (!callerFg && !fgRequired && == null && ()) { ProcessRecord proc = (, ); if (proc == null || () > PROCESS_STATE_RECEIVER) { // The app has not been created, or has been created, but no four major components are run in the background // ... // The delay service is not started here, just return if () { return ; } // If the timeout of the non-foreground service that is currently launched has exceeded the maximum number, then the current service must be set to delay Service // The delay service is not started here, so it returns directly if (() >= mMaxStartingBackground) { // Something else is starting, delay! (TAG_SERVICE, "Delaying start of: " + r); (r); = true; return ; } addToStarting = true; } else if (() >= ActivityManager.PROCESS_STATE_SERVICE) { // The app is in the background, but only the service and receiver are running addToStarting = true; } } // allowBackgroundActivityStarts is currently false if (allowBackgroundActivityStarts) { (backgroundActivityStartsToken); } // 2. Continue to the next stage of the startup process ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); return cmp; }
There is no service startup restriction now, and it is time to officially start. First update the ServiceRecord data. Note here that the parameters to be sent to the Service are saved using ServiceRecord#pendingStarts.
Next, there is a piece of code related to delaying Service and listening to background Services. This is an optimization made to optimize the situation where Service is too launched. For developers of system optimization, you may want to focus on it. This article will ignore this optimization function.
Then continue to look at the next startup process
// ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { synchronized () { final ServiceState stracker = (); if (stracker != null) { (true, (), ); } } // Whether to call onStart() = false; final int uid = ; final String packageName = (); final String serviceName = (); (FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName, serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START); (uid, packageName, serviceName); // 1. Pull up Service String error = bringUpServiceLocked(r, (), callerFg, false /* whileRestarting */, false /* permissionsReviewRequired */, false /* packageFrozen */, true /* enqueueOomAdj */); /* Will be a no-op if nothing pending */ (OomAdjuster.OOM_ADJ_REASON_START_SERVICE); if (error != null) { return new ComponentName("!!", error); } // Optimize the situation of too many service startups in the background if ( && addToStarting) { boolean first = () == 0; (r); // Set a timeout time = () + .BG_START_TIMEOUT; if (first) { (); } } else if (callerFg || ) { (r); } return ; }
It's very simple. This step is mainly to pull up the Service through bringUpServiceLocked()
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired, boolean packageFrozen, boolean enqueueOomAdj) throws TransactionTooLargeException { // 1. If the Service has been started once, just send the send parameters to the Service directly // Because only Service has been started, it is not null if ( != null && () != null) { sendServiceArgsLocked(r, execInFg, false); return null; } // ... final boolean isolated = (&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; final String procName = ; HostingRecord hostingRecord = new HostingRecord("service", ); ProcessRecord app; if (!isolated) { // 1. The Service process exists, but the Service has not been started yet, so notify the host process to start the Service // Since we have already judged that it is not null, the situation here is that the Service has not been started app = (procName, ); if (app != null) { // The process is rough final IApplicationThread thread = (); final int pid = (); final UidRecord uidRecord = (); if (thread != null) { // attachment application has been completed try { (, , ); realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg, enqueueOomAdj); return null; } // ... } } } else { // ... } // 3. The process where the Service is located is not running, so you need to fork a process if (app == null && !permissionsReviewRequired && !packageFrozen) { // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service // was initiated from a notification tap or not. if ((app = (procName, , true, intentFlags, hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated)) == null) { String msg = "Unable to launch app " + + "/" + + " for service " + () + ": process is bad"; (TAG, msg); bringDownServiceLocked(r, enqueueOomAdj); return msg; } if (isolated) { = app; } } // For the front service app, temporarily added to the power saving mode whitelist if () { (, SERVICE_START_FOREGROUND_TIMEOUT, REASON_SERVICE_LAUNCH, "fg-service-launch", TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, ); } // 3.1 mPendingServices Save the Service waiting for the process // After the process starts, this Service will be automatically started when executing the attachment application process. if (!(r)) { (r); } if () { // ... } return null; }
There are several situations to pull up a service
- If the Service has been started, just send the parameters to the Service to execute the task. This is the easiest case.
- If the Service has not been started, but the Service's host process exists, then notify the host process to create the Service and then send the parameters to it.
- If the host process of the Service does not exist, you must first fork a process, and then use mPendingServices to save the service waiting for the process to start. When the process starts, AMS will automatically complete the service startup process during the execution of the attachment application.
The third case obviously includes the previous two cases, so the following is a direct analysis of this process.
Starting of the host process
After the service host process starts, during the process of executing the attachment application, AMS will automatically start the services waiting for the process, as follows
private boolean attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) { // ... try { // ... // Process initialization if (() != null) { // This is an isolated process which should just call an entry point instead of // being bound to an application. ( (), ()); } else if (instr2 != null) { (processName, appInfo, providerList, , profilerInfo, , , , testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, (), new Configuration(().getConfiguration()), (), getCommonServicesLocked(), (), buildSerial, autofillOptions, contentCaptureOptions, (), serializedSystemFontMap); } else { (processName, appInfo, providerList, null, profilerInfo, null, null, null, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, (), new Configuration(().getConfiguration()), (), getCommonServicesLocked(), (), buildSerial, autofillOptions, contentCaptureOptions, (), serializedSystemFontMap); } // ... } catch (Exception e) { // ... return false; } // ... if (!badApp) { try { // Start the Service of the waiting process didSomething |= (app, processName); checkTime(startTime, "attachApplicationLocked: after "); } } // ... return true; }
Finally, ActiveServices#attachApplicationLocked() is called to complete the service startup
// boolean attachApplicationLocked(ProcessRecord proc, String processName) throws RemoteException { boolean didSomething = false; // mPendingServices saves the services waiting for the process to begin if (() > 0) { ServiceRecord sr = null; try { // traverse mPendingServices and start all Services belonging to the process for (int i=0; i<(); i++) { sr = (i); // Filter out Services that do not belong to this process if (proc != && ( != || !())) { continue; } final IApplicationThread thread = (); final int pid = (); final UidRecord uidRecord = (); (i); i--; (, , ); // Start Service realStartServiceLocked(sr, proc, thread, pid, uidRecord, , true); didSomething = true; if (!isServiceNeededLocked(sr, false, false)) { bringDownServiceLocked(sr, true); } (OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } } catch (RemoteException e) { // ... } } // Service that needs to be restarted by processing process restart if (() > 0) { // ... } return didSomething; }
Finally, we see what we want to see, first filter out the service that is not part of the process from mPendingServices, and then start it
// private void realStartServiceLocked(ServiceRecord r, ProcessRecord app, IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg, boolean enqueueOomAdj) throws RemoteException { // ... // Note that the value is not null, that is, ServiceRecord#app not null // Therefore, it can be determined by judging whether the Service has been started (app, thread, pid, uidRecord); = = (); final ProcessServiceRecord psr = ; final boolean newService = (r); // Note that the Service timeout function is implemented here bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */); (app, false, null); updateServiceForegroundLocked(psr, /* oomAdj= */ false); // Force an immediate oomAdjUpdate, so the client app could be in the correct process state // before doing any service related transactions (app); (app, OomAdjuster.OOM_ADJ_REASON_START_SERVICE); boolean created = false; try { // ... // 1. Notify the host process to create a Service (r, , (), ()); (); created = true; // ... } catch (DeadObjectException e) { // ... } finally { if (!created) { // ... } } // ... // 2. Send parameters to Service sendServiceArgsLocked(r, execInFg, true); if () { // ... } if () { // ... } }
Now, formally interact with the host process to start Service
- First, IApplicationThread#scheduleCreateService() is used to notify the host process to create a Service.
- Then, the parameters are sent to the host process through IApplicationThread#scheduleServiceArgs(), and the host process will send the parameters to the Service to perform tasks.
Host process creation Service
After receiving the instruction to create a Service, the host process will send a message through the Handler and finally call the following method
// private void handleCreateService(CreateServiceData data) { unscheduleGcIdler(); LoadedApk packageInfo = getPackageInfoNoCheck( , ); Service service = null; try { // Create Application and call Application#onCreate() Application app = (false, mInstrumentation); final cl; if ( != null) { cl = (); } else { cl = (); } // Create Service service = () .instantiateService(cl, , ); ContextImpl context = (service .createServiceBaseContext(this, packageInfo)); if ( != null) { context = (ContextImpl) (); } if ( != null &amp;&amp; &gt; 0) { final String attributionTag = [0]; context = (ContextImpl) (attributionTag); } ().addLoaders( ().getLoaders().toArray(new ResourcesLoader[0])); (service); (context, this, , , app, ()); // Execute Service#onCreate() (); (, data); (, service); // Notify the server service to be created successfully try { ().serviceDoneExecuting( , SERVICE_DONE_EXECUTING_ANON, 0, 0); } catch (RemoteException e) { throw (); } } catch (Exception e) { // ... } }
Host inheritance will first create Application, execute Application#onCreate(), then create Service, execute Service#onCreate(), and finally notify the server that the Service was created successfully. This process is simply smooth.
Service Receive parameters
After the host process receives the Service startup parameters sent by the server, it finally calls the following method
// private void handleServiceArgs(ServiceArgsData data) { CreateServiceData createData = (); Service s = (); if (s != null) { try { if ( != null) { (()); (isProtectedComponent(), ()); } int res; if (!) { // Call Service#onStartCommand() receives parameters and executes tasks // Note that this is in the main thread res = (, , ); } else { (); res = Service.START_TASK_REMOVED_COMPLETE; } (); try { // Put ServiceThe execution result of #onStartCommand() is fed back to the server // Because this result affects the way Service restarts ().serviceDoneExecuting( , SERVICE_DONE_EXECUTING_START, , res); } catch (RemoteException e) { throw (); } } catch (Exception e) { // ... } } }
When the host process receives the service startup parameters, it calls Service#onStartCommand() to receive the parameters and executes the task.
Note that Service#onStartCommand() is executed in the main thread. In fact, the life cycle of the four major components is executed in the main thread. Everyone should keep this in mind.
Then return the result of Service#onStartCommand() to the server. The return result of this method is important, and it affects how the Service restarts. Here is a brief display of the results processing flow
// void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res, boolean enqueueOomAdj) { boolean inDestroying = (r); if (r != null) { if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) { // Note that ServiceRecord#callStart , set to true after the Service has executed onStartCommand() and feedback the results. = true; switch (res) { case Service.START_STICKY_COMPATIBILITY: // Service will restart, but the original startup parameters will not be received case Service.START_STICKY: { // Note that the last parameter is true // It indicates that the parameters for this execution in #deliveredStarts // Because this parameter is not required when Service restarts (startId, false, true); // Don't stop if killed. = false; break; } case Service.START_NOT_STICKY: { // ... break; } // Service will restart and will also receive the original startup parameters case Service.START_REDELIVER_INTENT: { // Note the last parameter true // It means that the parameters for this execution in #deliveredStarts // Therefore, this parameter is required when the Service restarts si = (startId, false, false); if (si != null) { = 0; ++; // Don't stop if killed. = true; } break; } // ... } if (res == Service.START_STICKY_COMPATIBILITY) { = false; } } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) { // ... } final long origId = (); // Main update oom ajd serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj); (origId); } }
This involves the basic skills of the Service. The above shows the processing of two return values. Both return values represent that the Service killed due to insufficient memory will restart the Service when the memory is sufficient, but one return value needs to send the previous startup parameter again, while the other return value does not.
For the return value that needs to be sent, it is not necessary to delete the sent parameter from the Service#deliveredStart list, because it needs to be sent when Service restarts. Instead, you need to delete the sent parameter from the list because Service restarts no longer require it.
Finish
This article fully analyzes the service startup process, which is the most basic and requires mastery.
This article also mentioned the limitations of Service startup. For a professional developer, you can just abide by these restrictions, but for system developers, you may want to remove/modify/add some restrictions, so you have to study them yourself.
In fact, this article also has a topic worth discussing, that is, optimizing a large number of service startups. For developers who optimize the system, it should be mastered. If you are interested and have time later, I will add an article. For more information about the startup of ActivityManagerService Service, please pay attention to my other related articles!