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。
