简介
framework层Service相关的逻辑主要在ActiveServices类中。
分类
- 启动方式
- start方式启动:onCreate() → onStartCommand() → stopService → onDestroy()
- bind方式启动:onCreate() → onBind() → unbindService → onUnbind() → onDestroy()
- 运行方式(优先级)
- 前台Service:会在通知栏显示通知,优先级较高;anr超时时间为20s
- 后台Service:没有通知,优先级较低;anr超时时间为200s
常见的adb命令
- 启动指定Service
- adb shell am startservice -n serviceName
- 查看Service的dump信息
- adb shell dumpsys activity services
Service启动失败原因
一般都有如下log打印:
Slog.w(TAG, “Background start not allowed: service “+ service(启动的Intent) + " to " + r.shortInstanceName(Service名字 )+ " from pid=” + callingPid(调用方的pid) + " uid=” + callingUid(调用方的uid)+ " pkg=" + callingPackage(调用方的包名) + " startFg?=" + fgRequired(是否是前台Service));
具体的逻辑在ActiveServices的startServiceLocked方法里
启动后台Service
// startRequested表示是否start方式启动(此处首次启动暂未赋值所以为false,且service stop后会赋值为false),fgRequired表示是否前台Service
if (forcedStandby || (!r.startRequested && !fgRequired)) {
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName, r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.shortInstanceName + " from pid=" + callingPid + " uid=" + callingUid + " pkg=" + callingPackage + " startFg?=" + fgRequired);
......
}
后台启动Service && 不在电池优化白名单
如果当前app的uid不是active(处于后台),即使启动的是前台Service,也可能会失败,例如:
ActivityManager: Background start not allowed: service Intent { act=geofence_trigered cmp=com.xiaomi.smarthome/.scene.activity.GeoActionService (has extras) } to com.xiaomi.smarthome/.scene.activity.GeoActionService from pid=9233 uid=10270 pkg=com.xiaomi.smarthome startFg?=true
原理代码如下,下面的就是上面1中列出的代码:
final boolean bgLaunch = !mAm.isUidActiveLocked(r.appInfo.uid);
boolean forcedStandby = false;
if (bgLaunch && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
forcedStandby = true;
}
另外,如果进程的procState < 8 ,他在后台保留1min后就会进入idle状态,相应的Service也会被stop
Service发生ANR的原因
10s内未调用startForeground导致的ANR
I am_anr : [0,16206,com.android.mms,952745573,Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{3201640 u0 com.android.mms/com.xiaomi.mms.transaction.MxActivateService}]
以startForegroundService方式启动的Service,则需要在10s内调用startForeground方法,否则会发生ANR。一般会将该方法放到Service的onStartCommand中去执行,因为这种类型的ANR超时时间是从framework层开始通知app端执行Service的onStartCommand开始计算的。如果将该方法放到Service的onCreate中去执行,onStartCommand方法返回值是START_NOT_STICKY,进程被杀后Service重启不会回调onCreate方法,但是还会走ANR超时逻辑。
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg, boolean oomAdjusted) throws TransactionTooLargeException {
.......
while (r.pendingStarts.size() > 0) {
.....
if (r.fgRequired && !r.fgWaiting) {
if (!r.isForeground) {
scheduleServiceForegroundTransitionTimeoutLocked(r);
}
}
前台Service
设置前台Service
如果需要提高进程的优先级,避免由于低内存或其他厂商定制策略被系统查杀,可以设置Service为前台Service,app端通过调用startForeground方法实现。具体流程如下图1:
app端Service中调用startForeground
- 传入Notication的id和Notification的实例,前台Service的类型
- 如果是用startForegroundService方式启动的,则需要在service的onStartCommand方法中调用该startForeground方法,避免anr
//Service.java
public final void startForeground(int id, @NonNull Notification notification, @ForegroundServiceType int foregroundServiceType) {
try {
mActivityManager.setServiceForeground(new ComponentName(this, mClassName), mToken, id,
notification, 0, foregroundServiceType);
} catch (RemoteException ex) {
}
}
AMS中作中转,调用ActiveServices.java中的方法(因为app端进程只有AMS或ATMS的binder对象)
- 该操作持有AMS大锁
- 真正实现地方在ActiveServices中
// AMS.java
@Override
public void setServiceForeground(ComponentName className, IBinder token,
int id, Notification notification, int flags, int foregroundServiceType) {
synchronized(this) {
mServices.setServiceForegroundLocked(className, token, id, notification, flags, foregroundServiceType);
}
}
具体实现逻辑
下面可能会省略部分代码
public void setServiceForegroundLocked(ComponentName className, IBinder token,
int id, Notification notification, int flags, int foregroundServiceType) {
final int userId = UserHandle.getCallingUserId();
// 清除calling uid,避免uid权限校验通不过,清除后callingUid就时system server的uid 1000
final long origId = Binder.clearCallingIdentity();
try {
// 通过component ,token ,userId获取到service record
ServiceRecord r = findServiceLocked(className, token, userId);
if (r != null) {
setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType);
}
} finally {
// 恢复之前的calling uid
Binder.restoreCallingIdentity(origId);
}
}
- 传入的notification id为0表示当前service退出前台状态
- P或P之后前台service需要申请FOREGROUND_SERVICE权限
- 移除前台service的10s超时消息
- 更新isForeground为true
- 通知异步执行入队列
/**
* @param id Notification ID. Zero === exit foreground state for the given service.
*/
private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
Notification notification, int flags, int foregroundServiceType) {
if (id != 0) {
if (notification == null) {
throw new IllegalArgumentException("null notification");
}
// Instant apps need permission to create foreground services.
if (r.appInfo.isInstantApp()) {
.......
} else {
// P或P之后前台service需要申请FOREGROUND_SERVICE权限
if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
mAm.enforcePermission(
android.Manifest.permission.FOREGROUND_SERVICE,
r.app.pid, r.appInfo.uid, "startForeground");
}
// 一般传入的service type默认为FOREGROUND_SERVICE_TYPE_MANIFEST,而manifestType默认为0(none)
int manifestType = r.serviceInfo.getForegroundServiceType();
if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST) {
foregroundServiceType = manifestType;
}
// 这种异常一般较少
if ((foregroundServiceType & manifestType) != foregroundServiceType) {
throw new IllegalArgumentException("foregroundServiceType "
+ String.format("0x%08X", foregroundServiceType)
+ " is not a subset of foregroundServiceType attribute "
+ String.format("0x%08X", manifestType)
+ " in service element of manifest file");
}
// 后台启动的前台Service不得拥有“使用中权限”,下面有讲到
if (!r.mAllowWhileInUsePermissionInFgs) {
Slog.w(TAG,"Foreground service started from background can not have "
+ "location/camera/microphone access: service "
+ r.shortInstanceName);
}
}
boolean alreadyStartedOp = false;
boolean stopProcStatsOp = false;
//移除前台service 10s的超时消息。重置fgRequired 和 fgWaiting
if (r.fgRequired) {
r.fgRequired = false;
r.fgWaiting = false;
alreadyStartedOp = stopProcStatsOp = true;
mAm.mHandler.removeMessages(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
}
try {
boolean ignoreForeground = false;
......
// service的app不在电池优化白名单内&不是前台app启动,ignoreForeground赋值为true
if (!ignoreForeground
&& !appIsTopLocked(r.appInfo.uid)
&& appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
Slog.w(TAG,
"Service.startForeground() not allowed due to bg restriction: service "
+ r.shortInstanceName);
updateServiceForegroundLocked(r.app, false);
ignoreForeground = true;
}
if (!ignoreForeground) {
// 取消可能存在的notification
if (r.foregroundId != id) {
cancelForegroundNotificationLocked(r);
r.foregroundId = id;
}
// 给service的notification相关变量赋值
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
r.foregroundNoti = notification;
r.foregroundServiceType = foregroundServiceType;
// 前台service第一次设置会为false
if (!r.isForeground) {
final ServiceMap smap = getServiceMapLocked(r.userId);
if (smap != null) {
// 统计每个app前台service的数量
ActiveForegroundApp active = smap.mActiveForegroundApps
.get(r.packageName);
if (active == null) {
active = new ActiveForegroundApp();
active.mPackageName = r.packageName;
active.mUid = r.appInfo.uid;
active.mShownWhileScreenOn = mScreenOn;
if (r.app != null && r.app.uidRecord != null) {
active.mAppOnTop = active.mShownWhileTop =
r.app.uidRecord.getCurProcState()
<= ActivityManager.PROCESS_STATE_TOP;
}
active.mStartTime = active.mStartVisibleTime
= SystemClock.elapsedRealtime();
smap.mActiveForegroundApps.put(r.packageName, active);
requestUpdateActiveForegroundAppsLocked(smap, 0);
}
active.mNumActive++;
}
// 设置isForeground为true,第二次执行该方法后不必执行这些代码,且表示当前service在前台
r.isForeground = true;
........
}
// 每次执行该方法都会重新post通知,异步执行
r.postNotification();
if (r.app != null) {
// 更新进程优先级
updateServiceForegroundLocked(r.app, true);
}
getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
} else {
}
} finally {
.......
}
} else {
// notification id为0 ,是取消前台service的流程,下面讲
}
发送通知
- 通知入队等操作异步进行,避免死锁
- 通知没有对应的icon,打印警告信息,并给设置一个简陋的通知模板,告知用户当前正在运行该进程
V/ActivityManager: Attempted to start a foreground service (com.mfashiongallery.emag/.lks.minusonescreen.overlay.OverlayService) with a broken notification (no icon: Notification(channel=gallery_overlay_windows shortcut=null contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE))
// ServiceRecord.java
public void postNotification() {
final int appUid = appInfo.uid;
final int appPid = app.pid;
if (foregroundId != 0 && foregroundNoti != null) {
// Do asynchronous communication with notification manager to
// avoid deadlocks.
final String localPackageName = packageName;
final int localForegroundId = foregroundId;
final Notification _foregroundNoti = foregroundNoti;
final ServiceRecord record = this;
ams.mHandler.post(new Runnable() {
public void run() {
NotificationManagerInternal nm = LocalServices.getService(NotificationManagerInternal.class);
if (nm == null) {
return;
}
Notification localForegroundNoti = _foregroundNoti;
try {
if (localForegroundNoti.getSmallIcon() == null) {
// 通知没有对应icon,打印警告信息
Slog.v(TAG, "Attempted to start a foreground service (" + shortInstanceName + ") with a broken notification (no icon: " + localForegroundNoti + ")");
CharSequence appName = appInfo.loadLabel(ams.mContext.getPackageManager());
if (appName == null) {
appName = appInfo.packageName;
}
Context ctx = null;
try {
ctx = ams.mContext.createPackageContextAsUser(
appInfo.packageName, 0, new UserHandle(userId));
// 重新构建通知相关信息
Notification.Builder notiBuilder = new Notification.Builder(ctx,
localForegroundNoti.getChannelId());
// 设置icon为app应用信息的icon
notiBuilder.setSmallIcon(appInfo.icon);
// mark as foreground
notiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true);
// 构建pendingIntent
runningIntent.setData(Uri.fromParts("package", appInfo.packageName, null));
PendingIntent pi = PendingIntent.getActivityAsUser(ams.mContext, 0,
runningIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
UserHandle.of(userId));
// 设置系统的通知颜色
notiBuilder.setColor(ams.mContext.getColor(
com.android.internal
.R.color.system_notification_accent_color));
// 设置通知标题为“xxx 正在运行”
notiBuilder.setContentTitle(
ams.mContext.getString(
com.android.internal.R.string
.app_running_notification_title,
appName));
// 设置通知的内容"点按即可了解详情或停止应用",miui定制国内版本为"可能导致系统卡顿,降低待机时间,点按关闭"
// MIUI MOD START:
// notiBuilder.setContentText(
// ams.mContext.getString(
// com.android.internal.R.string
// .app_running_notification_text,
// appName));
if (miui.os.Build.IS_INTERNATIONAL_BUILD) {
notiBuilder.setContentText(
ams.mContext.getString(
com.android.internal.R.string
.app_running_notification_text,
appName));
} else {
notiBuilder.setContentText(
ams.mContext.getString(
com.android.internal.R.string
.app_running_notification_tip_text,
appName));
}
// END
notiBuilder.setContentIntent(pi);
localForegroundNoti = notiBuilder.build();
} catch (PackageManager.NameNotFoundException e) {
}
}
if (nm.getNotificationChannel(localPackageName, appUid,
localForegroundNoti.getChannelId()) == null) {
int targetSdkVersion = Build.VERSION_CODES.O_MR1;
try {
final ApplicationInfo applicationInfo =
ams.mContext.getPackageManager().getApplicationInfoAsUser(
appInfo.packageName, 0, userId);
targetSdkVersion = applicationInfo.targetSdkVersion;
} catch (PackageManager.NameNotFoundException e) {
}
if (targetSdkVersion >= Build.VERSION_CODES.O_MR1) {
throw new RuntimeException(
"invalid channel for service notification: "
+ foregroundNoti);
}
}
if (localForegroundNoti.getSmallIcon() == null) {
// Notifications whose icon is 0 are defined to not show
// a notification, silently ignoring it. We don't want to
// just ignore it, we want to prevent the service from
// being foreground.
throw new RuntimeException("invalid service notification: "
+ foregroundNoti);
}
// 通知入队
nm.enqueueNotification(localPackageName, localPackageName,
appUid, appPid, null, localForegroundId, localForegroundNoti,
userId);
foregroundNoti = localForegroundNoti; // save it for amending next time
} catch (RuntimeException e) {
Slog.w(TAG, "Error showing notification for service", e);
// 出现问题会stop server,阿里巴巴app就经常因为这个原因被杀
ams.mServices.killMisbehavingService(record,
appUid, appPid, localPackageName);
}
}
});
}
}
取消前台service
app端调用
取消前台service的流程和设置前台service的流程基本相同,传入的参数不同,如图2
- 传入的notification id为0
- notifications为null
- flags为1表示移除通知,0表示不移除
public final void stopForeground(@StopForegroundFlags int flags) {
try {
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, 0, null,
flags, 0);
} catch (RemoteException ex) {
}
}
ActiveServices中的流程
setServiceForegroundInnerLocked方法中notification id为0的else逻辑
- 自减mActiveForegroundApps中对应app的mNumActive,代表当前app的前台service -1
- 如果app的mNumActive <= 0,则将该app从mActiveForegroundApps中移除,并更新adj
- 传入的flags为1,则需要取消notification
// 取消前台service,如果当前service是前台service
if (r.isForeground) {
final ServiceMap smap = getServiceMapLocked(r.userId);
if (smap != null) {
// 更新mActiveForegroundApps中对应app的mNumActive
decActiveForegroundAppLocked(smap, r);
}
// 重置isForeground属性
r.isForeground = false;
.......
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
updateServiceForegroundLocked(r.app, true);
}
}
// 如果传入的flags为1,则要取消通知
if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
cancelForegroundNotificationLocked(r);
r.foregroundId = 0;
r.foregroundNoti = null;
} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
r.stripForegroundServiceFlagFromNotification();
if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
r.foregroundId = 0;
r.foregroundNoti = null;
}
}
}
移除通知
private void cancelForegroundNotificationLocked(ServiceRecord r) {
// notification id不为0
if (r.foregroundId != 0) {
// First check to see if this app has any other active foreground services
// with the same notification ID. If so, we shouldn't actually cancel it,
// because that would wipe away the notification that still needs to be shown
// due the other service.
ServiceMap sm = getServiceMapLocked(r.userId);
if (sm != null) {
for (int i = sm.mServicesByInstanceName.size() - 1; i >= 0; i--) {
ServiceRecord other = sm.mServicesByInstanceName.valueAt(i);
if (other != r && other.foregroundId == r.foregroundId
&& other.packageName.equals(r.packageName)) {
// Found one! Abort the cancel.
return;
}
}
}
r.cancelNotification();
}
}
public void cancelNotification() {
// 异步执行,避免死锁
final String localPackageName = packageName;
final int localForegroundId = foregroundId;
final int appUid = appInfo.uid;
final int appPid = app != null ? app.pid : 0;
ams.mHandler.post(new Runnable() {
public void run() {
NotificationManagerInternal nm = LocalServices.getService(
NotificationManagerInternal.class);
if (nm == null) {
return;
}
try {
nm.cancelNotification(localPackageName, localPackageName, appUid, appPid,
null, localForegroundId, userId);
} catch (RuntimeException e) {
Slog.w(TAG, "Error canceling notification for service", e);
}
}
});
}
后台启动的前台Service不得拥有使用中权限
android会限制从后台启动的前台service拥有“使用中”权限,包括位置、摄像头、麦克风等,否则会打印出如下的警告信息。
W ActivityManager: Foreground service started from background can not have location/camera/microphone access: service com.mi.health/.scenerecognition.SceneForegroundService
start或bind方式启动都会去给mAllowWhileInUsePermissionInFgs赋值
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs =
shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
callingUid, service, r, allowBackgroundActivityStarts);
}
豁免情况
- FGS由ROOT_UID,SYSTEM_UID,NFC_UID、SHELL_UID等启动。
- CallingUid 声明START_ACTIVITIES_FROM_BACKGROUND权限(hide权限)
- FGS由IME或其他可见应用启动。
- 在while-in-use白名单内:AttentionService, SystemCaptionService(for live caption)
private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
int callingPid, int callingUid, Intent intent, ServiceRecord r,
boolean allowBackgroundActivityStarts) {
// 一般后台启动前台service限制打开的
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
return true;
}
// 一般为false
if (allowBackgroundActivityStarts) {
return true;
}
//部分uid豁免
boolean isCallerSystem = false;
final int callingAppId = UserHandle.getAppId(callingUid);
switch (callingAppId) {
case ROOT_UID:
case SYSTEM_UID:
case NFC_UID:
case SHELL_UID:
isCallerSystem = true;
break;
default:
isCallerSystem = false;
break;
}
if (isCallerSystem) {
return true;
}
if (r.app != null) {
ActiveInstrumentation instr = r.app.getActiveInstrumentation();
if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
return true;
}
}
final boolean hasAllowBackgroundActivityStartsToken = r.app != null
? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false;
if (hasAllowBackgroundActivityStartsToken) {
return true;
}
// app声明了START_ACTIVITIES_FROM_BACKGROUND权限豁免
if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
return true;
}
// app在top自然允许
final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
if (isCallingUidTopApp) {
return true;
}
// Does the calling UID have any visible activity?
final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
if (isCallingUidVisible) {
return true;
}
final boolean isWhiteListedPackage =
mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage);
if (isWhiteListedPackage) {
return true;
}
// Is the calling UID a device owner app?
final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
if (isDeviceOwner) {
return true;
}
return false;
}
后台Service
后台Service管控
android对后台进程管控比较严格,app退到后台超过1min就会进入idle状态,并尝试stop相关的Service,所以我们可能会经常见到如下log:
08-27 20:08:12.038 1588 5729 I am_uid_active: 10135
08-27 20:09:13.517 1588 2114 I am_uid_idle: 10135
08-27 20:09:13.517 1588 2114 I am_stop_idle_service: [10135,com.android.htmlviewer/com.android.settings.services.MemoryOptimizationService]
为了便于理解,以下代码都只是节选方法中与之相关的一部分代码
在OomAdjuster.java的updateUidsLocked方法中发送IDLE_UIDS_MSG消息
- isProcStateBackground 是判断当前UidRecord的procState值是否小于8(PROCESS_STATE_TRANSIENT_BACKGROUND)
- 后台Service的procState一般是10(PROCESS_STATE_SERVICE)
- 如果当前进程跟top进程有绑定(如处于top状态的进程访问当前进程的provider等),那他的procState为3(PROCESS_STATE_BOUND_TOP)
- 如果当前UidRect处于后台且不在临时白名单内,但是上次不处于后台或者在临时白名单内,就会在1min(BACKGROUND_SETTLE_TIME)后将当前的UidRect idle
private void updateUidsLocked(ActiveUids activeUids, final long nowElapsed) {
for (int i = activeUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = activeUids.valueAt(i);
int uidChange = UidRecord.CHANGE_PROCSTATE;
// 判断当前procState是否处于后台或者不在临时白名单内
if (ActivityManager.isProcStateBackground(uidRec.getCurProcState()) && !uidRec.curWhitelist) {
// 上次procState不处于后台或者在临时白名单内
if (!ActivityManager.isProcStateBackground(uidRec.setProcState) || uidRec.setWhitelist) {
// 这里传入的elapsedRealtime而不是uptimeMillis,谷歌代码曾出过问。
uidRec.lastBackgroundTime = nowElapsed;
if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
// 发送uid idle消息,处于后台时间BACKGROUND_SETTLE_TIME是1min
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,mConstants.BACKGROUND_SETTLE_TIME);
}
}
在AMS的MainHandler处理该消息并调用idleUids去设置对应的uid idle
- idle处理逻辑是在system server的main线程中进行的
- idle时间是在处于后台或不在临时白名单的1min后
- 遍历active uid时,时间到了就执行doStopUidLocked
- 时间未到计算更新为最近的一次idle时间并再次发送IDLE_UIDS_MSG消息
final class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case IDLE_UIDS_MSG: {
idleUids();
} break;
@GuardedBy("mService")
void idleUidsLocked() {
final int N = mActiveUids.size();
if (N <= 0) {
return;
}
final long nowElapsed = SystemClock.elapsedRealtime();
// 得到起始后台时间点边界,大于这个值此时不会被idle
final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
long nextTime = 0;
for (int i = N - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
final long bgTime = uidRec.lastBackgroundTime;
// 上次处于后台时间 > 0表示当前uid处于后台
if (bgTime > 0 && !uidRec.idle) {
// 处于后台时间超过1min
if (bgTime <= maxBgTime) {
// 打印event log : am_uid_idle
EventLogTags.writeAmUidIdle(uidRec.uid);
// 给idle标志位赋值
uidRec.idle = true;
uidRec.setIdle = true;
// stop 后台Service
mService.doStopUidLocked(uidRec.uid, uidRec);
} else {
// 计算下次idle消息时间点
if (nextTime == 0 || nextTime > bgTime) {
nextTime = bgTime;
}
}
}
}
// 发送下次idle消息
if (nextTime > 0) {
mService.mHandler.removeMessages(IDLE_UIDS_MSG);
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
}
}
stop后台service的逻辑
- AMS的doStopUidLocked方法会先stop 后台service,再去更新uid并分发uid的change
- stopInBackgroundLocked回调后,service不一定会真被stop
//AMS.java
@GuardedBy("this")
final void doStopUidLocked(int uid, final UidRecord uidRec) {
mServices.stopInBackgroundLocked(uid);
enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
}
// ActiveServices.java
void stopInBackgroundLocked(int uid) {
// 获取该uid下的所有service
ServiceMap services = mServiceMap.get(UserHandle.getUserId(uid));
ArrayList<ServiceRecord> stopping = null;
if (services != null) {
// 遍历所获取到的services
for (int i = services.mServicesByInstanceName.size() - 1; i >= 0; i--) {
ServiceRecord service = services.mServicesByInstanceName.valueAt(i);
// start方式启动的service
if (service.appInfo.uid == uid && service.startRequested) {
// 获取当前service的start 权限,miui会多传入callingPackage,对诸如小米推送拉起的service放开权限
if (mAm.getAppStartModeLocked(service.appInfo.uid, service.packageName,
service.appInfo.targetSdkVersion, -1, false, false, false)
!= ActivityManager.APP_START_MODE_NORMAL) {
if (stopping == null) {
stopping = new ArrayList<>();
}
String compName = service.shortInstanceName;
// 打印event log am_stop_idle_service
EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
// 打印idle相关的system log
StringBuilder sb = new StringBuilder(64);
sb.append("Stopping service due to app idle: ");
UserHandle.formatUid(sb, service.appInfo.uid);
sb.append(" ");
TimeUtils.formatDuration(service.createRealTime
- SystemClock.elapsedRealtime(), sb);
sb.append(" ");
sb.append(compName);
Slog.w(TAG, sb.toString());
// 將当前service添加进stoppping列表
stopping.add(service);
// 如果当前app不在电池优化白名单中,那么前台service也是有可能不允许启动的,所以要取消notification
if (appRestrictedAnyInBackground(service.appInfo.uid, service.packageName)) {
cancelForegroundNotificationLocked(service);
}
}
}
}
if (stopping != null) {
for (int i=stopping.size()-1; i>=0; i--) {
ServiceRecord service = stopping.get(i);
service.delayed = false;
services.ensureNotStartingBackgroundLocked(service);
// stop service
stopServiceLocked(service);
}
}
}
}
private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
boolean hasConn) {
if (isServiceNeededLocked(r, knowConn, hasConn)) {
return;
}
// 如果有新拉起service的需求,本次不会stop该service
if (mPendingServices.contains(r)) {
return;
}
bringDownServiceLocked(r);
}