Android startForegroundService详解
10:45 am
Monday, 13 June 2022 (HKT)
Time in Hong Kong
概述
基于Android 10分析startForegroundService的基本流程。由于大致流程与startService一致,因此本文重点分析foreground流程的主要差异点,分析Android系统对foreground带来的限制(Service.onCreate()后限定时间内调用startForeground()
以及没有满足条件时系统停止service的实现、foreground service必须搭配Notification的限制条件)。
Android O之后系统禁止后台应用启动后台服务,且启动前台服务必须具有权限:
1 | <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> |
在Foreground Service启动后5s内必须调用Service.startForeground()
,否则会因为超出timeout而被系统stop。
过程
startForegroundService大体流程上与startService一致,仅多了一个Foreground标志和相关的检查。
Context.startForegroundService
类图、流程图:与startService一致。(可以参考Android startService流程详解)
startForegroundService()
与startService()
都是调用startServiceCommon(Intent, foreground :Boolean, UserHandle)
来实现的。唯一的差异是startForegroundService()
对该方法的foreground
参数传递为true。
SystemServer
同样的,在SystemServer
中AMS
的处理流程基本一致。在ActiveService
中处理带foreground
请求的startService。
ActiveServices
- 检查调用者是否为前台进程,设置到
callerFg :boolean
ActiveServices
根据Intent获取ServiceRecord
后。- 检查是否为后台启动并设置到
bgLaunch :boolean
中。后台启动的标准是isUidActiveLocked()
。它和callerFg
有所不同。callerFg
为是否是前台进程,而uid active则取决于正在运行的组件也同属于这个Uid。 - 当
bgLaunch
为true时,属于后台启动服务。进一步调用appRestrictedAnyInBackground(uid, pkgName)
来检查是否要推迟启动(forceStandby :boolean
)。该方法内部通过AppOps
检查权限AppOpsManager.OP_RUN_ANY_IN_BACKGROUN
来实现。其条件为:是否为persistent app、是否在background白名单、是否在Device idle白名单中。 - 如果service申请了foreground标志,接着通过
AppOps
检查对应的包是否有OP_START_FOREGROUND
权限。如果没有权限,则会取消掉fgRequired
即将start请求处理为非foreground的,同时设置forceSlientAbort
为true。 - 如果
forceStandBy
为true,或者是fgRequired
为false(申请的是后台启动)且startRequesed
为false,那么本次启动就是后台进程启动的后台服务,是会被拒绝启动的。 - 上一步执行完成后,实际上已经完成了对调用者是否允许启动前台服务完成了检查。接下来执行启动过程都不会再care。
fgRequired
标志会设置到ServiceRecord
对应的成员。系统一些记录使用记录、耗电记录、活动记录的服务会记录该前台服务的活动。除此之外也没有其他对foreground start的操作,像普通服务一样执行。- 和
startService
一致,接着进入startServiceInnerLocked()
内部的bringUpServiceLocked()
。 - 在
bumpServiceExecutingLocked()
中埋下Service启动超时机制以及Service.startForeground()
的超时机制——调用scheduleServiceTimeoutLocked()
向AMS
的Handler发送延时消息SERVICE_TIMEOUT_MSG
。其中前台启动服务对应的延时时长为20秒,后台启动为200秒。 - 如果在timeout之前该Message没有被移除,那么回调
AMS.serviceTimeout(ProcessRecord)
,内部调用ProcessRecord.appNotResponding()
来触发ANR。 - 上面发出20s timeout后(后台service是200s),与后台服务一样回调App的
onCreate()
和onStartCommand()
。在onStartCommand()回调中,Service必须调用startForeground()
来移除Message,防止timeout触发ANR。
startForeground
foreground service与普通startService流程一致。主要差异在于Service需要调用startForeground()
。
startForeground()
通过Binder调用AMS.setServiceForeground()
,其内部调用ActiveServices.setServiceForegroundLocked()
、setServiceForegroundInnerLocked()
。该方法内部移除了ANR timeout Message。并且检查是否允许成为前台服务,设置为前台服务并显示Notification。