Android 中service 详解 中说明了大概背景,这里不再过叙述了。
代码基于Android O
Android基础总结之六:Sevice 中是应用端对于service 使用的总结,其中看到启动service 需要的接口有startService 和bindService。在Android O 中又添加了一个接口api——startForegroundService。本篇主要围绕对两个start service接口进行解析。
上层启动service 直接接口在Context 中:
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
@Override
public ComponentName startForegroundService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, true, mUser);
}
@Override
public ComponentName startServiceAsUser(Intent service, UserHandle user) {
return startServiceCommon(service, false, user);
}
@Override
public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
return startServiceCommon(service, true, user);
}
之前我们知道的是在启动一个service 的时候需要调用接口startService 或者bindService,为什么这里多了Foreground service?
在官方文档 Android 8.0 行为变更 中有这样一段话:
Android 8.0 有一项复杂功能;系统不允许后台应用创建后台服务。 因此,Android 8.0 引入了一种全新的方法,即 Context.startForegroundService()
,以在前台启动新服务。
在系统创建服务后,应用有五秒的时间来调用该服务的 startForeground()
方法以显示新服务的用户可见通知。
如果应用在此时间限制内未调用 startForeground()
,则系统将停止服务并声明此应用为 ANR。
回过头来对比下startService 和startForegroundService ,主要区别就是第二个参数,如果是前台服务,第二个参数为true。这里留意下,下面进一步解析时会用到。这两个函数最终调用的地方是相同的,都是函数startServiceCommon() :
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();
}
}
看到这里大概猜到 Android 中service 详解 其中的几个问题都是从这里抛出来的。
根本原因是AMS 调用startService 的返回值ComponentName 不是我们想要的,也就是说后面的处理肯定会创建一个ComponentName,package name 为 ! 、!! 和 ? 三个中的一个,而class name 会作为异常message 抛出。
还有一点,通过函数validateServiceIntent() 可以看到如果SDK 版本大于L 的,要求service 不能隐式启动。
private void validateServiceIntent(Intent service) {
if (service.getComponent() == null && service.getPackage() == null) {
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
IllegalArgumentException ex = new IllegalArgumentException(
"Service Intent must be explicit: " + service);
throw ex;
} else {
Log.w(TAG, "Implicit intents with startService are not safe: " + service
+ " " + Debug.getCallers(2, 3));
}
}
}
总结:
1、service 启动入口startService 和startForegroundService,其中startForegroundService 为O 版本才出现的。(bindService 下一篇介绍)
2、两个接口最终调用的地方是相同的,都是AMS 的startService,对于foreground service 参数requireForeground 为true。
从上面Context 中startServiceCommon() 接口确定下startService 传入的参数:
确定参数后就可以放心进入source code 了:
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;
}
}
省略掉之前判断的条件,该函数最终调用的应该是ActiveServices 中的startServiceLocked,参数都是直传的,多加了callingPid,callingUid。
函数比较多,我们这里分步来解析。
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=" + callingPid
+ ") when starting service " + service);
}
callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
} else {
callerFg = true;
}
这个callerFg 标记当前caller app 是前台还是后台。
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false);
if (res == null) {
return null;
}
if (res.record == null) {
return new ComponentName("!", res.permission != null
? res.permission : "private to package");
}
注意最后一个参数,判断是否是bind external service,如果该service 可以运行在外部进程中,那么servcie 在注册的时候需要置上flag FLAG_EXTERNAL_SERVICE。这样bindService 的时候将最后一个参数传入为true,就可以实现external service。
if (r == null && !isBindExternal) {
Intent.FilterComparison filter = new Intent.FilterComparison(service);
r = smap.mServicesByIntent.get(filter);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by intent: " + r);
}
if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
&& !callingPackage.equals(r.packageName)) {
// If an external service is running within its own package, other packages
// should not bind to that instance.
r = null;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Whoops, can't use existing external service");
}
如果service 为FLAG_EXTERNAL_SERVICE,在startService() 的时候isBindExternal 为false,走的是上面一个case,如果是bindService(),最后走的是后面一个case,变量 r 最终为null,需要后面重现生成。
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
if (isBindExternal) {
if (!sInfo.exported) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not exported");
}
if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not an isolatedProcess");
}
// Run the service under the calling package's application.
ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
if (aInfo == null) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
"could not resolve client package " + callingPackage);
}
sInfo = new ServiceInfo(sInfo);
sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
sInfo.applicationInfo.packageName = aInfo.packageName;
sInfo.applicationInfo.uid = aInfo.uid;
name = new ComponentName(aInfo.packageName, name.getClassName());
service.setComponent(name);
} else {
throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
name);
}
这边就可以到,后面会重新生成一个ComponentName,package name 为当前bind service 的app 的package,class name是原来service 的name。
而且exported 需要置为true,否则会出现SecurityException。而对于startService 来说这个值直接设为false 即可。
详细code 这里暂不做分析,需要知道service 所有信息是在这里获取的,包括service 所需的permission check 也是在这里进行。如果出现异常,会导致该函数的返回值为null,或者是ServiceRecord 为null。那如果ServiceRecord 为null,就会出现code 中的package name 为 "!",最终会导致 java.lang.SecurityException: Not allowed to start service
这个函数主要判断当前应用是否可以唤醒后台服务,这个函数还是有些东西的。
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
packageTargetSdk);
这里传入的alwaysRestrict 为false,需要通过函数appServicesRestrictedInBackgroundLocked 进一步确认:
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
if (mPackageManagerInt.isPackagePersistent(packageName)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " is persistent; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Non-persistent but background whitelisted?
if (uidOnBackgroundWhitelist(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on background whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Is this app on the battery whitelist?
if (isOnDeviceIdleWhitelistLocked(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on idle whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
这些条件可以创建条件让应用满足启动后台服务,如果一般应用,这些条件都无法满足了。
在该应用不满足上面 3.3.1 条件时,会继续调用appRestrictedInBackgroundLocked()
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
}
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
// ...and legacy apps get an AppOp check
int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
uid, packageName);
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
}
switch (appop) {
case AppOpsManager.MODE_ALLOWED:
return ActivityManager.APP_START_MODE_NORMAL;
case AppOpsManager.MODE_IGNORED:
return ActivityManager.APP_START_MODE_DELAYED;
default:
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
如果SDK 版本大于等于Android O,直接放回APP_START_MODE_DELAYED_RIGID,也就是O 以上版本的应用在不满足上面3.3.1 中的条件时,是不允许启动后台服务的。
不过庆幸的是,对于Android O 之前版本的应用,会通过AppOpsManager 确认是否可以启动后台服务,而AppOpsManager 中默认是允许的。
回归到ActiveServices 中,如果不让启动后台服务,或者Android O版本的应用,会进入下面的case :
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.name.flattenToShortString()
+ " from pid=" + callingPid + " uid=" + callingUid
+ " pkg=" + callingPackage);
if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
// In this case we are silently disabling the app, to disrupt as
// little as possible existing apps.
return null;
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
最终出现了package name为 "?" 的ComponentName。
从而抛出java.lang.IllegalStateException: Not allowed to start service Intent(java.lang.IllegalStateException: Not allowed to start service Intent)
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
r.fgRequired = fgRequired;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid));
回归到ActiveServices中,这里有些对于ServiceRecord 的赋值,后面处理的时候很重要。
例如后面在bringUpServiceLocked() 中需要知道有没有pendingStarts。
这个函数是start service 的核心处理部分,在这之前的都是一些条件的过滤。
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
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, 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() + mAm.mConstants.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.rescheduleDelayedStartsLocked();
}
} else if (callerFg || r.fgRequired) {
smap.ensureNotStartingBackgroundLocked(r);
}
return r.name;
代码不是很多,但是里面的信息却不少,我们来分步解析。
Service的start是由函数startServiceInnerLocked完成,而startServiceInnerLocked则是调用bringUpServiceLocked完成Service的start,每次startService都会往ServiceRecord的pendingStarts里填加一项StartItem,即使是被放入Delayed List的Service启动。bringUpServiceLocked做的事就是拉起Service。
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
如果r.app 也就是ServiceRecord 和thread 已经不为null,也就是说该service 已经create,再次调用startService() 函数的时候,会直接调用sendServiceArgsLocked(),这里暂时不介绍,下面会详细说明。
if (!whileRestarting && mRestartingServices.contains(r)) {
// If waiting for a restart, then do nothing.
return null;
}
这里whileRestarting 是传进来的,如果从startService 调用bring up,那么这个值为false,此时不用继续执行,等待restart 处理进来;如果这个值是从restart 调用的bring up,那么这个值为true,也就是进入了restart 流程,会继续往下执行。
if (mRestartingServices.remove(r)) {
clearRestartingIfNeededLocked(r);
}
如果从restart 调用的bring up,上面参数whileRestaring 为true,会继续执行代码。这里如果restart 开始执行,状态就都需要clear,不需要在处于restart 状态。
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}
需要直接start servicce,不在需要delayed 。
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);
}
}
} else {
app = r.isolatedProc;
if (WebViewZygote.isMultiprocessEnabled()
&& r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
hostingType = "webview_service";
}
}
如果service 的ProcessRecord 已经创建了,会直接调用realStartServiceLocked(),进入start service 的最终流程。下面详细解析这个函数。
如果ProcessRecord 还没有创建,那就会跳过上面这一段流程,继续往下执行。
if (app == null && !permissionsReviewRequired) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingType, r.name, false, isolated, false)) == null) {
...
}
...
}
...
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
如果app 为null,也就是service 还没有create,会调用AMS 中startProcessLocked() 创建,这个函数比较长,可以自行跟一下source code,主要是通过startProcessLocked() 创建进程,并加入到mPendingServices,等待attachApplicationLocked后再startService。
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);
}
}
如果之前有stop service 请求,这里会直接stop。
至此,bringUpServiceLocked() 函数解析完,主要确定ProcessRecord 是否创建完成,通过函数realStartServiceLocked()进入启动流程或者通过startProcessLocked()进入创建流程。这个函数的返回值如果不为null,外面会有exception 抛出:
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
if (error != null) {
return new ComponentName("!!", error);
}
下面对其中碰到的两个函数进行进一步解析:sendServiceArgsLocked 和 realStartServiceLocked。
3.5.1.1 sendServiceArgsLocked()
从代码中可以看到,如果进入bringUpServiceLocked(),发现service 已经create,这个时候会直接进入sendServiceArgsLocked函数,也就是说startService() 剩下来的处理都是在这里。
final int N = r.pendingStarts.size();
if (N == 0) {
return;
}
里面过滤一下,确认有service 需要start。
bumpServiceExecutingLocked(r, execInFg, "start");
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
+ why + " of " + r + " in app " + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
+ why + " of " + r.shortName);
long now = SystemClock.uptimeMillis();
if (r.executeNesting == 0) {
r.executeFg = fg;
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
}
if (r.app != null) {
r.app.executingServices.add(r);
r.app.execServicesFg |= fg;
if (r.app.executingServices.size() == 1) {
scheduleServiceTimeoutLocked(r.app);
}
}
} else if (r.app != null && fg && !r.app.execServicesFg) {
r.app.execServicesFg = true;
scheduleServiceTimeoutLocked(r.app);
}
r.executeFg |= fg;
r.executeNesting++;
r.executingStart = now;
}
上面bringUpServiceLocked() 最开始的时候说过,就是将DelayList 中的service 一个一个的拉起。
在这里会拉起一个timeout,一般的后台服务,默认是20秒。如果被触发,那么就只有ANR 等待了。
如果等待的是一个前台服务:
if (r.fgRequired && !r.fgWaiting) {
if (!r.isForeground) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
}
scheduleServiceForegroundTransitionTimeoutLocked(r);
} else {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Service already foreground; no new timeout: " + r);
}
r.fgRequired = false;
}
}
会拉起一个前台服务的timeout,默认时间为 5 秒。如果被触发,那么就只有ANR 等待了。
继续分析,接着会创建一个ServiceStartArgs:
args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
为了后面的启动:
r.app.thread.scheduleServiceArgs(r, slice);
app.thread是Service所在进程的IApplicationThread Binder对象,用于AMS的SystemServer进程到Client App端的跨进程调用,IApplicationThread的实现是在ActivityThread的内部类ApplicationThread,AMS -> ActivityThread的调用通过IApplicationThread,ActivityThread -> AMS的调用就是ActivityManagerNative,这样就打通了一条从AMS到ActivityThread的跨进程调用之路。
scheduleServiceArgs在ActivityThread里的对应就是ActivityThread.handleServiceArgs,这就执行到了我们所熟悉的onStartCommand。
至此可以解释两个我们对于Service的认知:
sendServiceArgsLocked之后,pendingStarts里的StartItem就被加入到了deliveredStarts里,等待后续stopService或者Service restart的时候用。
3.5.1.2 realStartServiceLocked()
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();
// create service
final boolean newService = app.services.add(r);
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(r.app, /* oomAdj= */ false);
mAm.updateOomAdjLocked();
boolean created = false;
try {
...
...
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 (r.whitelistManager) {
app.whitelistManager = true;
}
// bind service
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);
...
// start service
sendServiceArgsLocked(r, execInFg, true);
...
}
上面sendServiceArgsLocked() 是在service 已经被创建的情况下触发,这里是第一次start service时候,此时service 还没有被create。此时会调用 bumpServiceExecutingLocked(r, execInFg, "create");
接着:
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
这里的app.thread 在上面 3.5.1.1 中已经解释过。这里主要是调用了scheduleCreateService(),最终触发的是我们熟悉的onCreate()。
bindService() 最后也会进入这个函数,会通过:
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);
详细看 Android service 启动篇之 bindService
接着:
sendServiceArgsLocked(r, execInFg, true);
同样这里也会有这个调用,通过 3.5.1.1 我么知道,这里会拉起一个timeout,最终调用的熟悉的onStartCommand()。
注意:
bind service 的时候也会进入该函数,但是 3.4节中变量是没有赋值,所以最终进入函数也会return。
详细看 Android service 启动篇之 bindService
至此,startService 的整个过程大概分析完成。