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

同样的,在SystemServerAMS的处理流程基本一致。在ActiveService中处理带foreground请求的startService。

ActiveServices

  1. 检查调用者是否为前台进程,设置到callerFg :boolean
  2. ActiveServices根据Intent获取ServiceRecord后。
  3. 检查是否为后台启动并设置到bgLaunch :boolean中。后台启动的标准是isUidActiveLocked()。它和callerFg有所不同。callerFg为是否是前台进程,而uid active则取决于正在运行的组件也同属于这个Uid。
  4. bgLaunch为true时,属于后台启动服务。进一步调用appRestrictedAnyInBackground(uid, pkgName)来检查是否要推迟启动(forceStandby :boolean)。该方法内部通过AppOps检查权限AppOpsManager.OP_RUN_ANY_IN_BACKGROUN来实现。其条件为:是否为persistent app、是否在background白名单、是否在Device idle白名单中。
  5. 如果service申请了foreground标志,接着通过AppOps检查对应的包是否有OP_START_FOREGROUND权限。如果没有权限,则会取消掉fgRequired即将start请求处理为非foreground的,同时设置forceSlientAbort为true。
  6. 如果forceStandBy为true,或者是fgRequired为false(申请的是后台启动)且startRequesed为false,那么本次启动就是后台进程启动的后台服务,是会被拒绝启动的。
  7. 上一步执行完成后,实际上已经完成了对调用者是否允许启动前台服务完成了检查。接下来执行启动过程都不会再care。
  8. fgRequired标志会设置到ServiceRecord对应的成员。系统一些记录使用记录、耗电记录、活动记录的服务会记录该前台服务的活动。除此之外也没有其他对foreground start的操作,像普通服务一样执行。
  9. startService一致,接着进入startServiceInnerLocked()内部的bringUpServiceLocked()
  10. bumpServiceExecutingLocked()中埋下Service启动超时机制以及Service.startForeground()的超时机制——调用scheduleServiceTimeoutLocked()AMS的Handler发送延时消息SERVICE_TIMEOUT_MSG。其中前台启动服务对应的延时时长为20秒,后台启动为200秒。
  11. 如果在timeout之前该Message没有被移除,那么回调AMS.serviceTimeout(ProcessRecord),内部调用ProcessRecord.appNotResponding()来触发ANR。
  12. 上面发出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。

参考

  1. Android startService服务启动流程