Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
// If we're starting indirectly (e.g. from PendingIntent), figure out whether // we're launching into an app in a background state. This keys off of the same // idleness state tracking as e.g. O+ background service start policy. finalbooleanbgLaunch= !mAm.isUidActiveLocked(r.appInfo.uid);
// If the app has strict background restrictions, we treat any bg service // start analogously to the legacy-app forced-restrictions case, regardless // of its target SDK version. booleanforcedStandby=false; if (bgLaunch && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { ... forcedStandby = true; }
// If this is a direct-to-foreground start, make sure it is allowed as per the app op. booleanforceSilentAbort=false; if (fgRequired) { finalintmode= mAm.mAppOpsService.checkOperation( AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName); switch (mode) { case AppOpsManager.MODE_ALLOWED: case AppOpsManager.MODE_DEFAULT: // All okay. break; case AppOpsManager.MODE_IGNORED: // Not allowed, fall back to normal start service, failing siliently // if background check restricts that. ... fgRequired = false; forceSilentAbort = true; break; default: returnnewComponentName("!!", "foreground not allowed as per app op"); } }
// If this isn't a direct-to-foreground start, check our ability to kick off an // arbitrary service if (forcedStandby || (!r.startRequested && !fgRequired)) { // Before going further -- if this app is not allowed to start services in the // background, then at this point we aren't going to let it period. finalintallowed= mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName, r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { ... if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) { // In this case we are silently disabling the app, to disrupt as // little as possible existing apps. returnnull; } if (forcedStandby) { // This is an O+ app, but we might be here because the user has placed // it under strict background restrictions. Don't punish the app if it's // trying to do the right thing but we're denying it for that reason. if (fgRequired) { ... returnnull; } } // This app knows it is in the new model where this operation is not // allowed, so tell it what has happened. UidRecorduidRec= mAm.mProcessList.getUidRecordLocked(r.appInfo.uid); returnnewComponentName("?", "app is in background uid " + uidRec); } }
// At this point we've applied allowed-to-start policy based on whether this was // an ordinary startService() or a startForegroundService(). Now, only require that // the app follow through on the startForegroundService() -> startForeground() // contract if it actually targets O+. if (r.appInfo.targetSdkVersion < Build.VERSION_CODES.O && fgRequired) { ... fgRequired = false; }
// If permissions need a review before any of the app components can run, // we do not start the service and launch a review activity if the calling app // is in the foreground passing it a pending intent to start the service when // review is completed.
// XXX This is not dealing with fgRequired! if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage, callingUid, service, callerFg, userId)) { returnnull; }
if (fgRequired) { // We are now effectively running a foreground service. ... mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService), AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, true); }
finalServiceMapsmap= getServiceMapLocked(r.userId); booleanaddToStarting=false; if (!callerFg && !fgRequired && r.app == null && mAm.mUserController.hasStartedUserState(r.userId)) { ProcessRecordproc= mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false); if (proc == null || proc.getCurProcState() > 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 (r.delayed) { // This service is already scheduled for a delayed start; just leave // it still waiting. return r.name; } if (smap.mStartingBackground.size() >= mMaxStartingBackground) { // Something else is starting, delay! ... smap.mDelayedStartList.add(r); r.delayed = true; return r.name; } ... addToStarting = true; } elseif (proc.getCurProcState() >= 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 (allowBackgroundActivityStarts) { r.whitelistBgActivityStartsOnServiceStart(); }
if (!whileRestarting && mRestartingServices.contains(r)) { // If waiting for a restart, then do nothing. returnnull; } ... // We are now bringing the service up, so no longer in the // restarting state. if (mRestartingServices.remove(r)) { clearRestartingIfNeededLocked(r); }
// Make sure this service is no longer considered delayed, we are starting it now. if (r.delayed) { ... getServiceMapLocked(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.mUserController.hasStartedUserState(r.userId)) { Stringmsg= ...; ... 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) {...}
// 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; if (WebViewZygote.isMultiprocessEnabled() && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) { hostingRecord = HostingRecord.byWebviewZygote(r.instanceName); } if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) { hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName, r.definingUid); } }
// Not running -- get it started, and enqueue this service record // to be executed when the app comes up. if (app == null && !permissionsReviewRequired) { if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, hostingRecord, false, isolated, false)) == null) { Stringmsg= ...; bringDownServiceLocked(r); return msg; } if (isolated) { r.isolatedProc = app; } }
if (r.fgRequired) { mAm.tempWhitelistUidLocked(r.appInfo.uid, SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch"); }
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) { stopServiceLocked(r); } }
if (newService && created) { app.addBoundClientUidsOfNewService(r); }
// 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(newServiceRecord.StartItem(r, false, r.makeNextStartId(), null, null, 0)); }
sendServiceArgsLocked(r, execInFg, true);
调用 requestServiceBindingsLocked,准备调用 onBind 方法;
调用 sendServiceArgsLocked,准备调用 onStartCommand 方法;
1 2 3 4 5 6 7 8 9 10 11 12 13
if (r.delayed) { getServiceMapLocked(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) { stopServiceLocked(r); } } }
privatevoidhandleCreateService(CreateServiceData data) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler();
while (r.pendingStarts.size() > 0) { ServiceRecord.StartItemsi= r.pendingStarts.remove(0); if (si.intent == null && N > 1) { // If somehow we got a dummy null intent in the middle, // then skip it. DO NOT skip a null intent when it is // the only one in the list -- this is to support the // onStartCommand(null) case. continue; } ...... bumpServiceExecutingLocked(r, execInFg, "start"); if (!oomAdjusted) { oomAdjusted = true; mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } if (r.fgRequired && !r.fgWaiting) { if (!r.isForeground) { scheduleServiceForegroundTransitionTimeoutLocked(r); } else { r.fgRequired = false; } } intflags=0; if (si.deliveryCount > 1) { flags |= Service.START_FLAG_RETRY; } if (si.doneExecutingCount > 0) { flags |= Service.START_FLAG_REDELIVERY; } args.add(newServiceStartArgs(si.taskRemoved, si.id, flags, si.intent)); }
if (s.app != null) { ... // This could have made the service more important. mAm.updateLruProcessLocked(s.app, (callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities()) || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0), b.client); mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE); }
if (s.app != null && b.intent.received) { // Service is already running, so we can immediately // publish the connection. try { c.conn.connected(s.name, b.intent.binder, false); } catch (Exception 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) { requestServiceBindingLocked(s, b.intent, callerFg, true); } } elseif (!b.intent.requested) { requestServiceBindingLocked(s, b.intent, callerFg, false); }
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 = newConnectionInfo(); info.binder = service; info.deathMonitor = newDeathMonitor(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); } }
处理新连接:
创建新 ConnectionInfo,存入要连接的服务和死亡回调信息;
服务调用 linkToDeath 注册死亡回调;
将新的 ConnectionInfo 存入 mActiveConnections;
如果发生异常,远程对象已死,则移除之前添加的 ConnectionInfo;
如果之前持有旧连接,注销旧连接的服务的死亡回调;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 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) { mConnection.onServiceConnected(name, service); } else { // The binding machinery worked, but the remote returned null from onBind(). mConnection.onNullBinding(name); } }
if (b.connections.size() == 0) { b.intent.apps.remove(b.client); }
if (!c.serviceDead) { if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0 && b.intent.hasBound) { try { bumpServiceExecutingLocked(s, false, "unbind"); ... // 调整 LruProcess 和 OomAdj,又来一遍? b.intent.hasBound = false; // Assume the client doesn't want to know about a rebind; // we will deal with that later if it asks for one. b.intent.doRebind = false; s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent()); } catch (Exception e) { serviceProcessGoneLocked(s); } }
// If unbound while waiting to start, remove the pending service mPendingServices.remove(s);
booleaninDestroying= mDestroyingServices.contains(r); if (b != null) { if (b.apps.size() > 0 && !inDestroying) { // Applications have already bound since the last // unbind, so just rebind right here. booleaninFg=false; for (int i=b.apps.size()-1; i>=0; i--) { ProcessRecordclient= b.apps.valueAt(i).client; if (client != null && client.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND) { inFg = true; break; } } try { requestServiceBindingLocked(r, b, inFg, true); } catch (TransactionTooLargeException e) { // Don't pass this back to ActivityThread, it's unrelated. } } else { // Note to tell the service the next time there is // a new client. b.doRebind = true; } }
privatefinalbooleanisServiceNeededLocked(ServiceRecord r, boolean knowConn, boolean hasConn) { // Are we still explicitly being asked to run? if (r.startRequested) { returntrue; }
// Is someone still bound to us keeping us running? if (!knowConn) { hasConn = r.hasAutoCreateConnections(); } if (hasConn) { returntrue; }
publicbooleanhasAutoCreateConnections() { // XXX should probably keep a count of the number of auto-create // connections directly in the service. for (int conni=connections.size()-1; conni>=0; conni--) { ArrayList<ConnectionRecord> cr = connections.valueAt(conni); for (int i=0; i<cr.size(); i++) { if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) { returntrue; } } } returnfalse; }
privatefinalvoidbringDownServiceLocked(ServiceRecord r) { ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections(); for (intconni= connections.size() - 1; conni >= 0; conni--) { ArrayList<ConnectionRecord> c = connections.valueAt(conni); for (int i=0; i<c.size(); i++) { ConnectionRecordcr= c.get(i); // There is still a connection to the service that is // being brought down. Mark it as dead. cr.serviceDead = true; cr.stopAssociation(); try { cr.conn.connected(r.name, null, true); } catch (Exception e) {...} } }
// Check to see if the service had been started as foreground, but being // brought down before actually showing a notification. That is not allowed. if (r.fgRequired) { r.fgRequired = false; r.fgWaiting = false; ... mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService), AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName); mAm.mHandler.removeMessages( ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r); if (r.app != null) { Messagemsg= mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG); msg.obj = r.app; msg.getData().putCharSequence( ActivityManagerService.SERVICE_RECORD_KEY, r.toString()); mAm.mHandler.sendMessage(msg); } }
// Note when this method is called by bringUpServiceLocked(), the service is not found // in mServicesByInstanceName and found will be null. if (found != null && found != r) { thrownewIllegalStateException(...); } smap.mServicesByIntent.remove(r.intent); r.totalRestartCount = 0; unscheduleServiceRestartLocked(r, 0, true);
// Also make sure it is not on the pending list. for (int i=mPendingServices.size()-1; i>=0; i--) { if (mPendingServices.get(i) == r) { mPendingServices.remove(i); } }
if (mAm.mAtmInternal.isShuttingDown()) { returnfalse; }
ServiceMapsmap= getServiceMapLocked(r.userId); if (smap.mServicesByInstanceName.get(r.instanceName) != r) { returnfalse; }
finallongnow= SystemClock.uptimeMillis();
if ((r.serviceInfo.applicationInfo.flags &ApplicationInfo.FLAG_PERSISTENT) == 0) { // Any delivered but not yet finished starts should be put back // on the pending list. ......
booleanattachApplicationLocked(ProcessRecord proc, String processName) throws RemoteException { booleandidSomething=false; // Collect any services that are waiting for this process to come up. if (mPendingServices.size() > 0) { ServiceRecordsr=null; try { for (int i=0; i<mPendingServices.size(); i++) { sr = mPendingServices.get(i); if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid || !processName.equals(sr.processName))) { continue; }
mPendingServices.remove(i); i--; proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode, mAm.mProcessStats); realStartServiceLocked(sr, proc, sr.createdFromFg); didSomething = true; if (!isServiceNeededLocked(sr, false, false)) { // We were waiting for this service to start, but it is actually no // longer needed. This could happen because bringDownServiceIfNeeded // won't bring down a service that is pending... so now the pending // is done, so let's drop it. bringDownServiceLocked(sr); } } } catch (RemoteException e) { throw e; } }
// Also, if there are any services that are waiting to restart and // would run in this process, now is a good time to start them. It would // be weird to bring up the process but arbitrarily not let the services // run at this point just because their restart time hasn't come up. if (mRestartingServices.size() > 0) { ServiceRecord sr; for (int i=0; i<mRestartingServices.size(); i++) { sr = mRestartingServices.get(i); if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid || !processName.equals(sr.processName))) { continue; } mAm.mHandler.removeCallbacks(sr.restarter); mAm.mHandler.post(sr.restarter); } } return didSomething; }