一、背景
Android 10+对应用后台启动Activity施加了限制,限制有助于最大限度地减少对用户造成的干扰,并且可以让用户更好地控制其屏幕上显示的内容。
本文基于Android 13,分析并梳理Android对应用后台启动Activity的限制实现及例外条件。
二、限制点源码流程梳理
2.1 系统日志
Background activity start [
callingPackage: com.xxx.xxx;
callingUid: 10121;
appSwitchState: 1;
isCallingUidForeground: false;
callingUidHasAnyVisibleWindow: false;
callingUidProcState: LAST_ACTIVITY;
isCallingUidPersistentSystemProcess: false;
realCallingUid: 10121;
isRealCallingUidForeground: false;
realCallingUidHasAnyVisibleWindow: false;
realCallingUidProcState: PERSISTENT;
isRealCallingUidPersistentSystemProcess: true;
originatingPendingIntent: PendingIntentRecord{5ba206e com.xxx.xxx startActivity};
allowBackgroundActivityStart: false; intent: Intent { flg=0x10020004 cmp=com.xxx.xxx/.TargetActivity (has extras) }; callerApp: null; inVisibleTask: false]
通过日志定位到方法:com.android.server.wm.ActivityStarter#shouldAbortBackgroundActivityStart
2.2 围绕后台activity限制的核心调用栈

核心流程:
1)根据启动信息构建Request(1-6)

2)启动前校验 (7-11)
① 常规启动条件校验:
com.android.server.wm.ActivityTaskSupervisor#checkStartAnyActivityPermission
- checkPermission START_ANY_ACTIVITY 权限限制
- getComponentRestrictionForCallingPackage component限制
- getActionRestrictionForCallingPackage action限制
com.android.server.firewall.IntentFirewall#checkStartActivity
- checkIntent:检查intent是否有对应组件匹配规则
com.android.server.policy.PermissionPolicyService.Internal#checkStartActivity
- isActionRemovedForCallingPackage action是否被移除检查
② 后台启动activity校验:
com.android.server.wm.ActivityStarter#shouldAbortBackgroundActivityStart
By caller(callingUid)
- 身份校验:ROOT_UID、SYSTEM_UID、NFC_UID、home、IME(输入法)、persistent系统进程、recents、deviceOwner、companionApp;
- 权限校验:START_ACTIVITIES_FROM_BACKGROUND、SYSTEM_ALERT_WINDOW;
- 状态校验:应用/沙盒应用具有非应用类型可见窗;
By sender(realCallingUid) 主要是PendingIntent场景
- 状态校验:persistent系统进程且sender允许后台启动、满足BAL权限条件、具有非toast类型的可见窗、compaionApp
- callerApp特殊状态校验(areBackgroundActivityStartsAllowed=true):①10S内启动过或者finish的应用、②具有后台启动特权的应用、③在前台任务的返回栈中拥有 Activity的应用、④应用中的某个服务被另一个可见应用绑定、⑤应用token校验豁免。
三、限制点分析
注:callingUid:当前发起binder call到system_server的uid,realCallingUid: 最初发起整个binder call调用链的uid。举例:三方应用启动activity,callingUid和realCallingUid都是三方应用uid; 三方应用创建PendingIntent给系统Alarm启动,callingUid是三方应用,realCallingUid是1000;
3.1 By caller(callingUid)
3.1.1 身份校验
// 系统是否需要检查调用者的前台状态 不涉及PendingIntent 或者 没有ActivityOptions#setIgnorePendingIntentCreatorForegroundState设置,那一律需要检查
final boolean useCallingUidState = originatingPendingIntent == null || checkedOptions == null || !checkedOptions.getIgnorePendingIntentCreatorForegroundState();
if (useCallingUidState) {
if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID || callingAppId == Process.NFC_UID) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed for important callingUid (" + callingUid + ")");
}
return false;
}
// Always allow home application to start activities.
if (isHomeApp(callingUid, callingPackage)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed for home app callingUid (" + callingUid + ")");
}
return false;
}
// IME should always be allowed to start activity, like IME settings.
final WindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();
if (imeWindow != null && callingAppId == imeWindow.mOwnerUid) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed for active ime (" + callingUid + ")");
}
return false;
}
// don't abort if the caller has the same uid as the recents component
if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
+ ") is recents");
}
return false;
}
// don't abort if the callingUid is the device owner
if (mService.isDeviceOwner(callingUid)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
+ ") is device owner");
}
return false;
}
// don't abort if the callingUid has companion device
final int callingUserId = UserHandle.getUserId(callingUid);
if (mService.isAssociatedCompanionApp(callingUserId,
callingUid)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callingUid (" + callingUid
+ ") is companion app");
}
return false;
}
}
① 系统应用相关身份
ROOT_UID、SYSTEM_UID、NFC_UID、home、IME(输入法)、persistent系统进程、recents
② 应用是设备所有者
Device Owner 是在设备上以管理员身份运行的应用程序
Android提供了三种设备管理方案:
- Device Administration(设备管理员)
- Profile Owner(配置文件所有者)
- Device Owner(设备所有者)
这三种权限管理策略的能力大小依次增加 为 DeviceAdmin < ProfileOwner < DeviceOwner。
DeviceOwner功能:总得来说就是设置一些三方应用无法涉及到的软硬件的高级功能,也属于是一种身份的豁免。三方应用在常规场景下无法激活DeviceOwner角色。
③ 应用有配套硬件设备相关联
应用通过 CompanionDeviceManager API 实现与配套硬件设备相关联(包括: 蓝牙、WIFI、 NFC等),这里会统一拉起一个系统设置页
adb shell cmd companiondevice associate 0 com.stan.lab "5a:c6:40:4f:ba:fc"
adb shell cmd companiondevice list 0
// don't abort if the realCallingUid is an associated companion app
if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid),realCallingUid)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid + ") is companion app");
}
return false;
}
com.android.server.am.ActivityManagerService.LocalService#isAssociatedCompanionApp
public boolean isAssociatedCompanionApp(int userId, int uid) {
final Set<Integer> allUids = mCompanionAppUidsMap.get(userId);
if (allUids == null) {
return false;
}
return allUids.contains(uid);
}
mCompanionAppUidsMap
一个是配套设备配对功能扫描到设备且确认后会调用;另外一个是给系统应用提供的一个创建配套设备配对设备的接口createAssociation;第三个是给 adb shell cmd 命令使用的。而突破点在第一个调用者associate
追踪mCompanionAppUidsMap添加元素的调用路径(逆向调用栈):
com.android.server.wm.ActivityTaskManagerService.LocalService#setCompanionAppUids
com.android.server.companion.CompanionDeviceManagerService#updateAtm
com.android.server.companion.CompanionDeviceManagerService#updateAssociations
com.android.server.companion.CompanionDeviceManagerService#recordAssociation
com.android.server.companion.CompanionDeviceManagerService#addAssociation
addAssociation的调用有3处:
- CompanionDeviceManager#associate skipPrompt
- CompanionDeviceManager#createAssociation permission
- CompanionDeviceManagerService.ShellCmd#onCommand shell
3.1.2 权限校验
if (useCallingUidState) {
// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND " + "permission granted for uid " + callingUid);
}
return false;
}
// don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
Slog.w(TAG, "Background activity start for " + callingPackage+ " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
return false;
}
}
权限:
- START_ANY_ACTIVITY(算上前置的权限检查) 允许应用程序启动任何其他应用程序的 Activity,而无需经过正常的权限检查,三方应用无法获取
- START_ACTIVITIES_FROM_BACKGROUND 后台启动Acitivty权限,三方应用需要用户主动授权获取
- SYSTEM_ALERT_WINDOW 悬浮窗权限,三方应用需要用户主动授权获取
3.1.3 状态校验
① 应用/沙盒应用具有非应用类型可见窗
// This is used to block background activity launch even if the app is still
// visible to user after user clicking home button.
final int appSwitchState = mService.getBalAppSwitchesState();
// 该uid应用是否有可见activity 或 具有非应用类型可见窗
final boolean callingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid);
// 该uid应用是否为persistent的系统应用
final boolean isCallingUidPersistentSystemProcess = callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
// Normal apps with visible app window will be allowed to start activity if app switching
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
//
final boolean appSwitchAllowedOrFg = appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY;
final boolean allowCallingUidStartActivity = ((appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) && callingUidHasAnyVisibleWindow)
|| isCallingUidPersistentSystemProcess;
if (useCallingUidState && allowCallingUidStartActivity) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid
+ ", isCallingUidPersistentSystemProcess = "
+ isCallingUidPersistentSystemProcess);
}
return false;
}
// In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
// visible window.
// 宿主应用正确加载了配置了android:isolatedSdkProcess="true" 的沙盒化sdk,即满足沙盒应用判断条件
if (Process.isSdkSandboxUid(realCallingUid)) {
int realCallingSdkSandboxUidToAppUid = Process.getAppUidForSdkSandboxUid(
UserHandle.getAppId(realCallingUid));
if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: uid in SDK sandbox ("+ realCallingUid + ") has visible (non-toast) window.");
}
return false;
}
}
条件分析:(appSwitchAllowedOrFg || mService.mActiveUids.hasNonAppVisibleWindow(callingUid)) && callingUidHasAnyVisibleWindow
appSwitchState:用于控制 后台 Activity 启动限制(Background Activity Launcher, BAL) 中应用切换(App Switch)行为的状态变量。值有3个:
public static final int APP_SWITCH_ALLOW = 0;// 完全允许应用切换(默认状态)
public static final int APP_SWITCH_FG_ONLY = 1;// 仅允许前台应用切换
public static final int APP_SWITCH_DENY = 2;// 完全禁止应用切换(严格限制)
这个基本都是true,主要条件在于callingUidHasAnyVisibleWindow(mService.hasActiveVisibleWindow包含mService.mActiveUids.hasNonAppVisibleWindow(callingUid)):
boolean hasActiveVisibleWindow(int uid) {
if (mVisibleActivityProcessTracker.hasVisibleActivity(uid)) { //1、该uid应用是否有可见activity
return true;
}
return mActiveUids.hasNonAppVisibleWindow(uid);//2、当前应用具有非应用类型可见窗
}
// mActiveUids添加元素
void onSurfaceShownChanged(boolean shown) {
...
// Exclude toast because legacy apps may show toast window by themselves, so the misused
// apps won't always be considered as foreground state.
// Exclude private presentations as they can only be shown on private virtual displays and
// shouldn't be the cause of an app be considered foreground.
if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST
&& mAttrs.type != TYPE_PRIVATE_PRESENTATION) {
//hasNonAppVisibleWindow判断条件最终由addWindow 触发 WindowState#onSurfaceShownChanged
mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
}
}
3.2 By sender(realCallingUid)
3.2.1 状态校验
// Legacy behavior allows to use caller foreground state to bypass BAL restriction.
// ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED 值
final boolean balAllowedByPiSender = PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);
if (balAllowedByPiSender && realCallingUid != callingUid) {
// ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION 值
final boolean useCallerPermission = PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions);
if (useCallerPermission && ActivityManager.checkComponentPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
realCallingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
+ ") has BAL permission.");
}
return false;
}
// don't abort if the realCallingUid has a visible window
// TODO(b/171459802): We should check appSwitchAllowed also
// realCallingUid 有可见窗
if (realCallingUidHasAnyVisibleWindow) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
+ ") has visible (non-toast) window");
}
return false;
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
// allowBackgroundActivityStart是Request的属性,由ActivityStarter#setAllowBackgroundActivityStart设置
if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
+ ") is persistent system process AND intent sender allowed "
+ "(allowBackgroundActivityStart = true)");
}
return false;
}
// don't abort if the realCallingUid is an associated companion app
// realCallingUid 是设备配对应用
if (mService.isAssociatedCompanionApp(UserHandle.getUserId(realCallingUid),
realCallingUid)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
+ ") is companion app");
}
return false;
}
}
3.3 callerApp特殊状态校验
// If we don't have callerApp at this point, no caller was provided to startActivity().
// That's the case for PendingIntent-based starts, since the creator's process might not be
// up and alive. If that's the case, we retrieve the WindowProcessController for the send()
// caller if caller allows, so that we can make the decision based on its state.
int callerAppUid = callingUid;
if (callerApp == null && balAllowedByPiSender) {
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;
}
// don't abort if the callerApp or other processes of that uid are allowed in any way
if (callerApp != null && useCallingUidState) {
// first check the original calling process
// 1.
if (callerApp.areBackgroundActivityStartsAllowed(appSwitchState)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "Background activity start allowed: callerApp process (pid = "
+ callerApp.getPid() + ", uid = " + callerAppUid + ") is allowed");
}
return false;
}
// only if that one wasn't allowed, check the other ones
final ArraySet<WindowProcessController> uidProcesses = mService.mProcessMap.getProcesses(callerAppUid);
if (uidProcesses != null) {
for (int i = uidProcesses.size() - 1; i >= 0; i--) {
final WindowProcessController proc = uidProcesses.valueAt(i);
//2.
if (proc != callerApp && proc.areBackgroundActivityStartsAllowed(appSwitchState)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG,
"Background activity start allowed: process " + proc.getPid()
+ " from uid " + callerAppUid + " is allowed");
}
return false;
}
}
}
}
即:WindowProcessController.areBackgroundActivityStartsAllowed(appSwitchState)为true的条件:
boolean areBackgroundActivityStartsAllowed(int pid, int uid, String packageName,
int appSwitchState, boolean isCheckingForFgsStart,
boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
long lastStopAppSwitchesTime, long lastActivityLaunchTime,
long lastActivityFinishTime) {
// If app switching is not allowed, we ignore all the start activity grace period
// exception so apps cannot start itself in onPause() after pressing home button.
// APP_SWITCH_ALLOW = 0;// 完全允许应用切换(默认状态)
// APP_SWITCH_FG_ONLY = 1;// 仅允许前台应用切换
// APP_SWITCH_DENY = 2;// 完全禁止应用切换(严格限制)
if (appSwitchState == APP_SWITCH_ALLOW) {
// Allow if any activity in the caller has either started or finished very recently, and
// it must be started or finished after last stop app switches time.
// 1. 如果调用者中的任何activity最近10S内启动或finish,则允许后台启动
final long now = SystemClock.uptimeMillis();
if (now - lastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
|| now - lastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
// If activity is started and finished before stop app switch time, we should not
// let app to be able to start background activity even it's in grace period.
if (lastActivityLaunchTime > lastStopAppSwitchesTime || lastActivityFinishTime > lastStopAppSwitchesTime) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
}
return true;
}
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid + ")] Activity start within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period but also within stop app switch window");
}
}
}
// Allow if the proc is instrumenting with background activity starts privs.
// 2. 具有后台启动activity特权的Active Instrumentation所在进程
if (hasBackgroundActivityStartPrivileges) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process instrumenting with background " + "activity starts privileges");
}
return true;
}
// Allow if the caller has an activity in any foreground task.
// 3. 应用在前台任务的返回栈中拥有 Activity
if (hasActivityInVisibleTask && (appSwitchState == APP_SWITCH_ALLOW || appSwitchState == APP_SWITCH_FG_ONLY)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process has activity in foreground task");
}
return true;
}
// Allow if the caller is bound by a UID that's currently foreground.
// 4.应用中的某个服务被另一个可见应用绑定
if (isBoundByForegroundUid()) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process bound by foreground uid");
}
return true;
}
// Allow if the flag was explicitly set.
// 5.设置了BAL token
if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
if (DEBUG_ACTIVITY_STARTS) {
Slog.d(TAG, "[Process(" + pid + ")] Activity start allowed: process allowed by token");
}
return true;
}
return false;
}
① APP_SWITCH_ALLOW模式下,如果调用者中的任何activity最近10S内启动或finish,则允许后台启动:
一般用户正常使用设备的情况下都是APP_SWITCH_ALLOW,除非是以下特殊情况:
- 设备未处于锁屏状态
- 无特殊系统模式(如驾驶模式、紧急通话等)
- 未启用企业设备管理策略(MDM)
② 具有后台启动activity特权的Active Instrumentation所在进程
hasBackgroundActivityStartPrivileges=true的条件(直接与START_ACTIVITIES_FROM_BACKGROUND权限关联):
// Active instrumentation with background activity starts privilege running in process?
private volatile boolean mInstrumentingWithBackgroundActivityStartPrivileges;
public void setInstrumenting(boolean instrumenting, int sourceUid,
boolean hasBackgroundActivityStartPrivileges) {
checkArgument(instrumenting || sourceUid == -1);
mInstrumenting = instrumenting;
mInstrumentationSourceUid = sourceUid;
mInstrumentingWithBackgroundActivityStartPrivileges = hasBackgroundActivityStartPrivileges;
}
com.android.server.am.ProcessRecord#setActiveInstrumentation(ActiveInstrumentation instr) {
mInstr = instr;
boolean isInstrumenting = instr != null;
mWindowProcessController.setInstrumenting(
isInstrumenting,
isInstrumenting ? instr.mSourceUid : -1,
isInstrumenting && instr.mHasBackgroundActivityStartsPermission);
}
setActiveInstrumentation调用点:
activeInstr.mHasBackgroundForegroundServiceStartsPermission = checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid, callingUid)
app.setActiveInstrumentation(activeInstr);
对应:
// Whether the caller holds START_ACTIVITIES_FROM_BACKGROUND permission
com.android.server.am.ActiveInstrumentation#mHasBackgroundActivityStartsPermission
③ 应用在前台任务的返回栈中拥有 Activity
/**
* The state for oom-adjustment calculation. The higher 16 bits are the activity states, and the
* lower 16 bits are the task layer rank (see {@link Task#mLayerRank}). This field is written by
* window manager and read by activity manager.
*/
private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
com.android.server.wm.WindowProcessController#hasActivityInVisibleTask() {
return (mActivityStateFlags & ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK) != 0;
}
mActivityStateFlags的计算
void computeProcessActivityState() {
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
ActivityRecord.State bestInvisibleState = DESTROYED;
boolean allStoppingFinishing = true;
boolean visible = false;
int minTaskLayer = Integer.MAX_VALUE;
int stateFlags = 0;
final boolean wasResumed = hasResumedActivity();
final boolean wasAnyVisible = (mActivityStateFlags
& (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
for (int i = mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mActivities.get(i);
if (r.isVisible()) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE;
}
final Task task = r.getTask();
if (task != null && task.mLayerRank != Task.LAYER_RANK_INVISIBLE) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK;
}
if (r.isVisibleRequested()) {
if (r.isState(RESUMED)) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED;
}
if (task != null && minTaskLayer > 0) {
final int layer = task.mLayerRank;
if (layer >= 0 && minTaskLayer > layer) {
minTaskLayer = layer;
}
}
visible = true;
// continue the loop, in case there are multiple visible activities in
// this process, we'd find out the one with the minimal layer, thus it'll
// get a higher adj score.
} else if (!visible && bestInvisibleState != PAUSING) {
if (r.isState(PAUSING, PAUSED)) {
bestInvisibleState = PAUSING;
} else if (r.isState(STOPPING)) {
bestInvisibleState = STOPPING;
// Not "finishing" if any of activity isn't finishing.
allStoppingFinishing &= r.finishing;
}
}
}
stateFlags |= minTaskLayer & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
if (visible) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_VISIBLE;
} else if (bestInvisibleState == PAUSING) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
} else if (bestInvisibleState == STOPPING) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_STOPPING;
if (allStoppingFinishing) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING;
}
}
mActivityStateFlags = stateFlags;
...
}
④ 应用中的某个服务被另一个可见应用绑定或者系统拉起
- AccessibilityService
- AutofillService
- CallRedirectionService
- HostApduService
- InCallService
- TileService
- VoiceInteractionService
- VrListenerService
上述服务都需要申请相关权限,而对应权限全都是:signature 或 signatureOrSystem,三方应用无法申请。该条件相当于也是对系统应用豁免;
测试:应用在前台:oom_adj=0(前台) , 切到后台:700(后台),被其他应用绑定服务:100(可见),因此原则上还是在可见性上做文章。
注意 :从 Android 14 开始,如果绑定到该服务的应用以 Android 14 或更高版本为目标平台,它将不再允许具有该服务的应用默认启动后台 activity。应用必须传递 Context.BIND_ALLOW_ACTIVITY_STARTS 标志,以允许绑定服务应用启动后台 activity。
⑤ 设置了BAL token
com.android.server.wm.BackgroundLaunchProcessController#isBackgroundStartAllowedByToken (int uid, String packageName, boolean isCheckingForFgsStart) {
synchronized (this) {
if (mBackgroundActivityStartTokens == null
|| mBackgroundActivityStartTokens.isEmpty()) {
return false;
}
//false
if (isCheckingForFgsStart) {
// BG-FGS-start only checks if there is a token.
return true;
}
// 如果有token,没有callback,直接允许
if (mBackgroundActivityStartCallback == null) {
// We have tokens but no callback to decide => allow.
return true;
}
// The callback will decide.
// 如果有token,也有callback,由callback决定是否允许
return mBackgroundActivityStartCallback.isActivityStartAllowed(mBackgroundActivityStartTokens.values(), uid, packageName);
}
}
callback的唯一实现类:
private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
@Override
public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid,
String packageName) {
checkArgument(!tokens.isEmpty());
for (IBinder token : tokens) {
if (token != ALLOWLIST_TOKEN) {
// We only block or warn if the start is exclusively due to notification
return true;
}
}
String logcatMessage = "Indirect notification activity start (trampoline) from " + packageName;
if (blockTrampoline(uid)) {
Slog.e(TAG, logcatMessage + " blocked");
return false;
} else {
Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons");
return true;
}
}
private boolean blockTrampoline(int uid) {
if (mRoleObserver != null && mRoleObserver.isUidExemptFromTrampolineRestrictions(uid)) {
return CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK_FOR_EXEMPT_ROLES,
uid);
}
return CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
}
...
}
tokens 全局put的地方:
com.android.server.am.BroadcastQueue#maybeAddAllowBackgroundActivityStartsToken
com.android.server.am.ProcessRecord#addOrUpdateAllowBackgroundActivityStartsToken
com.android.server.am.ServiceRecord#setProcess