引言
点击此处查看《深入分析Android 9.0源代码》系列的组织结构和相关说明。
1 应用进程发起启动请求
本章的调用流程如下图所示:
注:
(1)由于编辑器渲染的时序图不支持“无触发对象消息”(在标准的时序图中,这种消息的起点为实心圆形,表示流程的开始),此处用“(Context)”虚指触发流程的起始对象(而不是Android中的Context类)。
(2)对于时序图中形如“A—[#num] method()—>B”的消息(连线为实线),它的含义是控制流由先前的对象A(直接)转移到对象B,然后调用对象B的编号为#num的method()方法。
1.1 startService@ContextWrapper
Android系统为开发者提供了startService和bindService两种启动Service的方式,其中前者对应于Context类的startService()
。由于Context类本身是一个抽象类,所以该方法实际上是在它的子类ContextWrapper中实现的。
// #1 {root}/core/java/ android.content.ContextWrapper (L663)
public ComponentName startService(Intent service) {
return mBase.startService(service); // 调用#2
}
【L-03】return mBase.startService(service);
mBase
是一个Context类型的成员变量,它的实际类型是ContextImpl。由此可见,Context抽象类提供了ContextWrapper和ContextImpl两个直接子类。其中ContextImpl是Context的功能实现类,它为Service等组件提供context这个重要概念的基础实现。但是,Android系统并不允许开发者直接使用ContextImpl类1,而是额外引入了一个ContextWrapper包装类(Activity、Service和Application类均直接或间接继承自该类),它负责将应用程序中与Context相关的方法调用转发给内部持有的ContextImpl引用。
1.2 startService@ContextImpl
startService()
对系统进程的Service启动请求打印额外的调试信息2,然后调用startServiceCommon()
处理启动请求。
// #2 {root}/core/java/ android.app.ContextImpl (L1530)
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser); // 调用#3
}
【L-04】return startServiceCommon(…, false, …);
此处调用startServiceCommon()
传入的第二个参数为false,表明默认情况下目标Service会以后台服务的方式进行启动。与前台服务相比,后台服务不会在下拉通知栏显示通知,同时优先级较低,当系统出现内存不足情况时容易被回收3。
1.3 startServiceCommon@ContextImpl
startServiceCommon()
首先对Intent的属性进行检查和准备,然后然后将Service的启动请求通过IPC(进程间通信)发送给系统服务ActivityManagerService(下文简称为AMS,它所属的system_server系统进程简称AMS进程)进行处理。由于AIDL实现的IPC默认是同步的,所以应用进程的当前线程将会挂起,直到AMS进程的startService()
返回。需要特别注意的是,虽然该方法本身是同步调用,但是后续AMS处理Service的启动过程是异步的,也即AMS进程的startService()
并不会一直阻塞直到Service启动完毕4。事实上,当应用进程调用的startServiceCommon()
返回时,其返回值仅包含需要启动的Service的相关信息(如果该Service存在),此时AMS并不保证目标Service已经创建成功(如果需要)并且执行了onStartCommand()
回调方法。
// #3 {root}/core/java/ android.app.ContextImpl (L1557)
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
...
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService(mMainThread.getApplicationThread(),
service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier()); // IPC调用#4
if (cn != null) {
... }
return cn;
}
【L-05】validateServiceIntent(service);
validateServiceIntent()
对参数Intent进行合法性检查,确保其中与Service启动的相关属性值不为null。此外,Android 5.0后强制要求Service必须通过显式Intent启动5,否则会直接抛出异常。
【L-06】service.prepareToLeaveProcess(this);
prepareToLeaveProcess()
对Intent的属性进行离开应用进程前的准备工作。在StrictMode下,该方法还会进行跨进程安全性的检查,此时不允许使用scheme为“file://”的URI进行数据传输,而必须使用FileProvider作为替代6。
【L-07】ComponentName cn = ActivityManager.getService().startService(…);
ActivityManager.getService()
获取AMS的Binder代理对象,然后通过AIDL调用AMS的startService()
。对AMS和IPC更详细的介绍参见本系列的《Activity启动流程》的1.3节。该方法的返回类型为ComponentName,它内部有两个String类型的成员变量mPackage
和mClass
,分别表示组件(此处为Service)对应的应用包名和组件类名7。
【L-10】if (cn != null) { … }
当由于权限、安全性等问题导致Service无法正常启动时,返回值cn
的成员变量mPackage
会被设置为"!"、“?”等特殊值,此时应用进程会进行处理并抛出对应的异常。
2 AMS处理启动请求
本章的调用流程如下图所示:
注:由于编辑器限制对象文本的最大长度,时序图中对于名称较长的类使用方括号(“[]”)和缩写来表示。上图中[AMS]=ActivityManagerService。
2.1 startService@ActivityManagerService
startService()
获取客户端进程的callingPid与callingUid,然后将它们连同其它参数传递给ActiveServices对象的startServiceLocked()
进行处理。
// #4 {root}/services/core/java com.android.server.am.ActivityManagerService (L20342)
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); // 调用#5
}
finally {
Binder.restoreCallingIdentity(origId);
}
return res;
}
}
【L-09】final long origId = Binder.clearCallingIdentity();
Binder.clearCallingIdentity()
将远程调用端的PID和UID的初始值暂存于返回值origId
中,然后将其替换为当前进程的PID和UID8。因为包括AMS在内的众多系统服务都运行在同一个system_server进程中,所以当AMS通过AIDL接口调用其它系统服务时,实质上只是一个同进程的本地方法调用。在这种情况下,Binder驱动不再(像跨进程调用一样)提供调用方进程的PID和UID,故此处需要显式地设置当前进程的PID和UID9。
【L-12】res = mServices.startServiceLocked(…);
mServices
是一个ActiveServices类型的成员变量,它的主要职责是辅助AMS管理应用服务10(与系统服务相对11)。
【L-16】Binder.restoreCallingIdentity(origId);
Binder.restoreCallingIdentity()
利用origId
恢复远程调用端的UID和PID的原始值,通常它与先前提到的Binder.clearCallingIdentity()
总是成对出现12。
2.2 startServiceLocked@ActiveServices
startServiceInnerLocked()
首先尝试获取目标Service对应的SerivceRecord缓存对象(如果不存在则创建一个新的SerivceRecord),然后检查启动权限的合法性13,最后判断Service能够立即启动还是需要延迟启动。
// #5 {root}/services/core/java com.android.server.am.ActiveServices (L389)
ComponentName startServiceLocked(IApplicationThread caller, Intent service,
String resolvedType, int callingPid, int callingUid, boolean fgRequired,
String callingPackage, final int userId) throws TransactionTooLargeException {
...
ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false, false);
...
ServiceRecord r = res.record;
...
r.startRequested = true;
...
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid));
.