Android 11 -- app 服务保活
app服务保活 (android 11 源码)
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
stop后台service的逻辑:
AMS的doStopUidLocked方法会先stop 后台service,再去更新uid并分发uid的change
stopInBackgroundLocked回调后,service不一定会真被stop
void stopInBackgroundLocked(int uid) {
// Stop all services associated with this uid due to it going to the background
// stopped state.
// 获取该uid下的所有service
ServiceMap services = mServiceMap.get(UserHandle.getUserId(uid));
ArrayList<ServiceRecord> stopping = null;
if (services != null) {
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) {
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;
//add 包名过滤防止它被杀
if(compName.contains("com.google.wangyiyun"))
continue;
//end
......
// 將当前service添加进stoppping列表
stopping.add(service);
// If the app is under bg restrictions, also make sure that
// any notification is dismissed
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);
}
}
}
}
//这个函数用停止app的服务
private void stopServiceLocked(ServiceRecord service) {
if (service.delayed) {
// If service isn't actually running, but is being held in the
// delayed list, then we need to keep it started but note that it
// should be stopped once no longer delayed.
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Delaying stop of pending: " + service);
service.delayedStop = true;
return;
}
StatsLog.write(StatsLog.SERVICE_STATE_CHANGED, service.appInfo.uid,
service.name.getPackageName(), service.name.getClassName(),
StatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
synchronized (service.stats.getBatteryStats()) {
service.stats.stopRunningLocked();
}
service.startRequested = false;
if (service.tracker != null) {
service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
service.callStart = false;
bringDownServiceIfNeededLocked(service, false, false);
}
bringDownServiceIfNeededLocked(...)
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);
}
(android-12)app进入后台,关掉杀死serivce
参考一
实际案例:Android 11 startService启动服务,应用置于后台超过1min,服务被销毁过程分析
Logcat打印:
W ActivityManager: Stopping service due to app idle: u0a137 -1m19s820ms com.xxx.factorysettings/.services.xxxService
app被切到后台,经过一分钟后,ActiveServices::stopInBackgroundLocked(...)
void stopInBackgroundLocked(int uid) {
//因为app将进入到后台,停止与此uid关联的所有服务 uid为app的uid
ServiceMap services = mServiceMap.get(UserHandle.getUserId(uid));
ArrayList<ServiceRecord> stopping = null;
if (services != null) {
for (int i = services.mServicesByInstanceName.size() - 1; i >= 0; i--) {
ServiceRecord service = services.mServicesByInstanceName.valueAt(i);
if (service.appInfo.uid == uid && service.startRequested) {
//判断条件:如果app的getAppStartModeLocked(...)不等于ActivityManager.APP_START_MODE_NORMAL,就要被stop
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;
EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
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());//打印日志标记点
//符合停止条件,就让他进入stop集合
stopping.add(service);
// If the app is under bg restrictions, also make sure that
// any notification is dismissed
if (appRestrictedAnyInBackground(
service.appInfo.uid, service.packageName)) {
cancelForegroundNotificationLocked(service);
}
}
}
}
if (stopping != null) {
//进入stop集合的service,开始逐个stopping(killer)
for (int i=stopping.size()-1; i>=0; i--) {
ServiceRecord service = stopping.get(i);
service.delayed = false;
services.ensureNotStartingBackgroundLocked(service);
stopServiceLocked(service);
}
}
}
}
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
if (mInternal.isPendingTopUid(uid)) {
return ActivityManager.APP_START_MODE_NORMAL;
}
//add text
if(packageName != null && packageName.equals("com.xx.xxxx")){
return ActivityManager.APP_START_MODE_NORMAL;
}
//add text
UidRecord uidRec = mProcessList.getUidRecordLocked(uid);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+ packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
+ (uidRec != null ? uidRec.idle : false));
....
实际案例:Android 11 后台启动服务 报错
Logcat:
W ActivityManager: Background start not allowed: service Intent
{ cmp=com.android.deskclock/.addition.resource.ResourceLoadService (has extras) } to ...
/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, final int userId,
boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
...
if (forcedStandby || (!r.startRequested && !fgRequired)) {
//检查是否允许app后台启动service
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
//sign 1.0 如果 allowed != ActivityManager.APP_START_MODE_NORMAL == false 不允许
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);
if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) {
// In this case we are silently disabling the app, to disrupt as
// little as possible existing apps.
return null;
}
if (forcedStandby) {
// This is an O+ app, but we might be here because the user has placed
// it under strict background restrictions. Don't punish the app if it's
// trying to do the right thing but we're denying it for that reason.
if (fgRequired) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.v(TAG, "Silently dropping foreground service launch due to FAS");
}
return null;
}
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
UidRecord uidRec = mAm.mProcessList.getUidRecordLocked(r.appInfo.uid);
return new ComponentName("?", "app is in background uid " + uidRec);
}
}
...
}
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
if (mInternal.isPendingTopUid(uid)) {
...
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
packageTargetSdk);
if (DEBUG_BACKGROUND_CHECK) {
Slog.d(TAG, "checkAllowBackground: uid=" + uid
+ " pkg=" + packageName + " startMode=" + startMode
+ " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid, false)
+ " onwhitelist(ei)=" + isOnDeviceIdleWhitelistLocked(uid, true));
}
//对低版本的兼容
if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
if (callingPid >= 0) {
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(callingPid);
}
if (proc != null &&
!ActivityManager.isProcStateBackground(proc.getCurProcState())) {
// Whoever is instigating this is in the foreground, so we will allow it
// to go through.
return ActivityManager.APP_START_MODE_NORMAL;
}
}
}
return startMode;
...
}
//1.Persistent app 允许 2.background白名单允许 3.battery白名单允许
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
if (mPackageManagerInt.isPackagePersistent(packageName)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " is persistent; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Non-persistent but background whitelisted?
if (uidOnBackgroundWhitelist(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on background whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Is this app on the battery whitelist?
if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on idle whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
//如果获取app的uid -->activityManagerService中提供的api
public String[] getPackagesForUid(int uid) {
return mActivityManagerService.mContext.getPackageManager().getPackagesForUid(uid);
}
ps -A | grep systemui
u0_a127(uid) 709 265 14421016 241628 0 0 S com.android.systemui
u0_a127(uid) 3333 265 14038800 180276 0 0 S com.android.systemui:screenshot
后台执行限制总结_background start not allowed: service intent
查询APP里是否有服务
Intent serviceIntent = new Intent();
String[] packageNames = {"com.tencent.qqmusic", "com.spotify.music","com.android.systemui"};
for (String pkgName : packageNames) {
serviceIntent.setPackage(pkgName);
List<ResolveInfo> resolveInfos = yourContext.getPackageManager().queryIntentServices(serviceIntent, 0);
Log.d(TAG, "Package: " + pkgName);
for (ResolveInfo resolveInfo : resolveInfos) {
Log.d(TAG, "Intent service name: " + resolveInfo.serviceInfo);
}
}
查询服务是否work
//从Android O开始,此方法不再可用于第三方应用程序。为了向后兼容,它仍然会返回调用者自己的服务
public static boolean isSeviceWorked(Context context, String serviceName) {
ActivityManager myManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
ArrayList<RunningServiceInfo> runningService = (ArrayList<RunningServiceInfo>) myManager
.getRunningServices(30);
for (int i = 0; i < runningService.size(); i++) {
if (runningService.get(i).service.getClassName().toString().equals(
serviceName)) {
return true;
}
}
return false;
}
Android T 关于系统应用内服务自启问题
Scheduling restart of crashed service解决方案与源码分析-CSDN博客
问题描述:
业务要求,在系统内开发一个app,因为要读写settings 属性的参数,所以给了android:sharedUserId="android.uid.system"和系统签名。
在最近任务界面,移除它的后台,发现它内部的一个服务会重启。但是服务onStartCommand的返回值用的是默认的return super.onStartCommand(intent, flags, startId);,奇怪?
//内置源码编译
android_app {
name: "xxApp",
resource_dirs: ["res"],
sdk_version: "current",
srcs: [
"src/**/*.java",
],
certificate: "platform",
product_specific: true,
static_libs: [
"androidx.annotation_annotation",
"androidx.lifecycle_lifecycle-common",
],
}
日志:
system_server W Scheduling restart of crashed service com.xx.xx/.service_broadcast.xx in 1000ms for start-requested
system_server I Start proc 3029:com.xx.xx/1000 for service {com.xx.xx/com.portworld.xx.service_broadcast.xx}
ps -A | grep xxx 查进程 ,发现它是system级别进程.
system 564 280 19034136 338168 do_epoll_wait 0 S system_server
u0_a73 752 280 16304600 219428 do_epoll_wait 0 S com.android.systemui
system 4248 280 14745008 137992 do_epoll_wait 0 S com.xxx.xxtapp
服务返回参数
一共四种模式,
START_STICKY (1)模式在服务死掉后被系统自动重启拉活,但是不会保留之前的intent参数;
START_STICKY_COMPATIBILITY (0)是START_STICKY 的兼容模式,不保证服务死掉后被系统自动拉活;
START_NOT_STICKY(2)服务死掉系统不会自动去拉活;
START_REDELIVER_INTENT(3)模式在服务死掉后被系统自动重启拉活,并且保留之前的intent参数。
源码跟踪
/** @return {@code true} if the restart is scheduled. */
private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allowCancel) {
android.util.Log.d("txx", Log.getStackTraceString(new Throwable()));
if (mAm.mAtmInternal.isShuttingDown()) {
Slog.w(TAG, "Not scheduling restart of crashed service " + r.shortInstanceName
+ " - system is shutting down");
return false;
}
...
android.util.Log.d("txx","allowCancel: "+allowCancel);
if (allowCancel) {
final boolean shouldStop = r.canStopIfKilled(canceled);
//START_STICKY类型canStopIfKilled方法为false,START_NOT_STICKY则为true
android.util.Log.d("txx","shouldStop: "+shouldStop);
if (shouldStop && !r.hasAutoCreateConnections()) {
// Nothing to restart.
return false;
}
reason = (r.startRequested && !shouldStop) ? "start-requested" : "connection";
} else {
reason = "always";
}
//下面就是服务重启逻辑
r.totalRestartCount++;
if (r.restartDelay == 0) {
r.restartCount++;
r.restartDelay = minDuration;
} else if (r.crashCount > 1) {
r.restartDelay = mAm.mConstants.BOUND_SERVICE_CRASH_RESTART_DURATION
* (r.crashCount - 1);
} else {
...
日志打印堆栈:
at com.android.server.am.ActiveServices.scheduleServiceRestartLocked(ActiveServices.java:3701)
at com.android.server.am.ActiveServices.killServicesLocked(ActiveServices.java:5556)
at com.android.server.am.ActivityManagerService.cleanUpApplicationRecordLocked(ActivityManagerService.java:12424)
at com.android.server.am.ActivityManagerService.handleAppDiedLocked(ActivityManagerService.java:3230)
at com.android.server.am.ActivityManagerService.appDiedLocked(ActivityManagerService.java:3337)
at com.android.server.am.ActivityManagerService$AppDeathRecipient.binderDied(ActivityManagerService.java:1478)
txx D allowCancel: true
txx D shouldStop: false
通过日志发现,shouldStop为false,所以它会进入到重启的逻辑。估计是system级别的Service默认被设定为了START_STICKY。
解决方案:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
...
//return super.onStartCommand(intent, flags, startId);
return START_NOT_STICKY;
}
PS: 自定义服务保活,把它搞成system级别 就行了 有点暴力了。

浙公网安备 33010602011771号