限制后台启动activity
如果未满足相关条件,则后台不允许启动activity,并会打印如下相关的log:
// anything that has fallen through would currently be abortedSlog.w(TAG,"Background activity start [callingPackage: "+ callingPackage
+"; callingUid: "+ callingUid
+"; appSwitchState: "+ appSwitchState
+"; isCallingUidForeground: "+ isCallingUidForeground
+"; callingUidHasAnyVisibleWindow: "+ callingUidHasAnyVisibleWindow
+"; callingUidProcState: "+DebugUtils.valueToString(ActivityManager.class,"PROCESS_STATE_", callingUidProcState)+"; isCallingUidPersistentSystemProcess: "+ isCallingUidPersistentSystemProcess
+"; realCallingUid: "+ realCallingUid
+"; isRealCallingUidForeground: "+ isRealCallingUidForeground
+"; realCallingUidHasAnyVisibleWindow: "+ realCallingUidHasAnyVisibleWindow
+"; realCallingUidProcState: "+DebugUtils.valueToString(ActivityManager.class,"PROCESS_STATE_", realCallingUidProcState)+"; isRealCallingUidPersistentSystemProcess: "+ isRealCallingUidPersistentSystemProcess
+"; originatingPendingIntent: "+ originatingPendingIntent
+"; allowBackgroundActivityStart: "+ allowBackgroundActivityStart
+"; intent: "+ intent
+"; callerApp: "+ callerApp
+"; inVisibleTask: "+(callerApp !=null&& callerApp.hasActivityInVisibleTask())+"]");
限制的例外情况
在 Android 10 或更高版本上运行的应用只有在满足以下一项或多项条件时,才能启动 Activity:
ROOT_UID/SYSTEM_UID/NFC_UID等重要uid
finalboolean 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 +")");}returnfalse;}......
home 进程
if(useCallingUidState){......if(isHomeApp(callingUid, callingPackage)){if(DEBUG_ACTIVITY_STARTS){Slog.d(TAG,"Activity start allowed for home app callingUid ("+ callingUid +")");}returnfalse;}
Ime窗口所在uid下的进程
if(useCallingUidState){......// IME should always be allowed to start activity, like IME settings.finalWindowState imeWindow = mRootWindowContainer.getCurrentInputMethodWindow();if(imeWindow !=null&& callingAppId == imeWindow.mOwnerUid){if(DEBUG_ACTIVITY_STARTS){Slog.d(TAG,"Activity start allowed for active ime ("+ callingUid +")");}returnfalse;}
应用具有可见窗口,例如前台 Activity
应用是persistent系统进程,并正在执行UI操作
// 是persistent系统进程,并正在执行UI操作finalboolean isCallingUidPersistentSystemProcess =
callingUidProcState <=ActivityManager.PROCESS_STATE_PERSISTENT_UI;// 如果允许应用程序切换,则允许具有可见应用程序窗口的普通应用程序启动活动,// 或者将允许具有非应用程序可见窗口的动态壁纸等应用程序。finalboolean appSwitchAllowedOrFg =
appSwitchState ==APP_SWITCH_ALLOW|| appSwitchState ==APP_SWITCH_FG_ONLY;finalboolean 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);}returnfalse;}
应用声明了START_ACTIVITIES_FROM_BACKGROUND 权限
特权system/priv-app/目录下的app才可以申请豁免
<!--@SystemApi@hideAllows an application tostart activities from background --><permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier|role"/>
if(useCallingUidState){// don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permissionif(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);}returnfalse;}
应用是Recent组件所在uid下进程(这里一般为home进程)
if(useCallingUidState){.......// don't abort if the caller has the same uid as the recents componentif(mSupervisor.mRecentTasks.isCallerRecents(callingUid)){if(DEBUG_ACTIVITY_STARTS){Slog.d(TAG,"Background activity start allowed: callingUid ("+ callingUid
+") is recents");}returnfalse;}
应用是设备所有者
应用是在设备所有者模式下运行的设备政策控制器。示例用例包括完全托管的企业设备,以及数字标识牌和自助服务终端等专用设备。
if(useCallingUidState){......// don't abort if the callingUid is the device ownerif(mService.isDeviceOwner(callingUid)){if(DEBUG_ACTIVITY_STARTS){Slog.d(TAG,"Background activity start allowed: callingUid ("+ callingUid
+") is device owner");}returnfalse;}
应用通过 CompanionDeviceManager API 与配套硬件设备相关联。
此 API 支持应用启动 API,以响应用户在配对设备上执行的操作。
if(useCallingUidState){......// don't abort if the callingUid has companion devicefinalint 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");}returnfalse;}
用户已向应用授予 SYSTEM_ALERT_WINDOW 权限
注意:在 Android 10(Go 版本)上运行的应用无法获得SYSTEM_ALERT_WINDOW权限。
注意:如果应用程序以 API 级别 23 或更高级别为目标,则应用程序用户必须通过权限管理屏幕明确向应用程序授予此权限。
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
android:protectionLevel="signature|setup|appop|installer|pre23|development"/>
if(useCallingUidState){......// don't abort if the callingUid has SYSTEM_ALERT_WINDOW permissionif(mService.hasSystemAlertWindowPermission(callingUid,
callingPid, callingPackage)){Slog.w(TAG,"Background activity start for "+ callingPackage
+" allowed because SYSTEM_ALERT_WINDOW permission is granted.");returnfalse;}}
关于areBackgroundActivityStartsAllowed系列
如果此时我们没有 callerApp,则没有向 startActivity() 提供调用者。 基于 PendingIntent 的启动就是这种情况,因为创建者的进程可能未启动且处于活动状态。 如果是这种情况,我们会在调用者允许的情况下为 send() 调用者检索 WindowProcessController,以便我们可以根据其状态做出决定。
// 遗留行为允许使用调用者前台状态绕过 BAL 限制。finalboolean balAllowedByPiSender =PendingIntentRecord.isPendingIntentBalAllowedByCaller(checkedOptions);int callerAppUid = callingUid;if(callerApp ==null&& balAllowedByPiSender){
callerApp = mService.getProcessController(realCallingPid, realCallingUid);
callerAppUid = realCallingUid;}if(callerApp !=null&& useCallingUidState){// first check the original calling processif(callerApp.areBackgroundActivityStartsAllowed(appSwitchState)){if(DEBUG_ACTIVITY_STARTS){Slog.d(TAG,"Background activity start allowed: callerApp process (pid = "+ callerApp.getPid()+", uid = "+ callerAppUid +") is allowed");}returnfalse;}// only if that one wasn't allowed, check the other onesfinalArraySet<WindowProcessController> uidProcesses =
mService.mProcessMap.getProcesses(callerAppUid);if(uidProcesses !=null){for(int i = uidProcesses.size()-1; i >=0; i--){finalWindowProcessController proc = uidProcesses.valueAt(i);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");}returnfalse;}}}}
应用的某个 Activity 刚在不久前启动/结束
如果不允许应用程序切换,我们将忽略所有启动activity宽限期异常,因此应用程序无法在按下主页按钮后在 onPause() 中自行启动。
在停止应用程序切换的时间之后启动或finish activity的10s内可后台启动Activity
booleanareBackgroundActivityStartsAllowed(int pid,int uid,String packageName,int appSwitchState,boolean isCheckingForFgsStart,boolean hasActivityInVisibleTask,boolean hasBackgroundActivityStartPrivileges,long lastStopAppSwitchesTime,long lastActivityLaunchTime,long lastActivityFinishTime){if(appSwitchState ==APP_SWITCH_ALLOW){// 如果调用者中的任何activity最近启动或finish,则允许,finallong now =SystemClock.uptimeMillis();// 启动或finish有10s宽限if(now - lastActivityLaunchTime <ACTIVITY_BG_START_GRACE_PERIOD_MS|| now - lastActivityFinishTime <ACTIVITY_BG_START_GRACE_PERIOD_MS){// 得在停止应用程序切换的时间之后启动或finish才行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");}returntrue;}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");}}}
具有后台启动activity特权的Active Instrumentation所在进程
// Allow if the proc is instrumenting with background activity starts privs.if(hasBackgroundActivityStartPrivileges){if(DEBUG_ACTIVITY_STARTS){Slog.d(TAG,"[Process("+ pid
+")] Activity start allowed: process instrumenting with background "+"activity starts privileges");}returntrue;}
应用在前台任务的返回栈中拥有 Activity
android U或许会取消
booleanhasActivityInVisibleTask(){return(mActivityStateFlags &ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK)!=0;}
// Allow if the caller has an activity in any foreground task.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");}returntrue;}
应用中的某个服务被另一个可见应用绑定
请注意,绑定到服务的应用必须保持可见,以便后台应用成功启动 Activity。
// Allow if the caller is bound by a UID that's currently foreground.if(isBoundByForegroundUid()){if(DEBUG_ACTIVITY_STARTS){Slog.d(TAG,"[Process("+ pid
+")] Activity start allowed: process bound by foreground uid");}returntrue;}privatebooleanisBoundByForegroundUid(){synchronized(this){if(mBoundClientUids !=null){for(int i = mBoundClientUids.size()-1; i >=0; i--){if(mUidHasActiveVisibleWindowPredicate.test(mBoundClientUids.get(i))){returntrue;}}}}returnfalse;}
/** A uid is considered to be foreground if it has a visible non-toast window. */@HotPath(caller =HotPath.START_SERVICE)booleanhasActiveVisibleWindow(int uid){if(mVisibleActivityProcessTracker.hasVisibleActivity(uid)){returntrue;}return mActiveUids.hasNonAppVisibleWindow(uid);}
应用收到系统的 PendingIntent 通知
对于服务和广播接收器的挂起 Intent,应用可在该挂起 Intent 发送几秒钟后启动 Activity
应用收到另一个可见应用发送的 PendingIntent
应用的 Service有带BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS的connection
需要声明START_ACTIVITIES_FROM_BACKGROUND权限
Android 10 (API 级别 29) 及更高版本对后台应用可启动 Activity 的时间施加限制。这些限制有助于最大限度地减少对用户造成的中断,并且可以让用户更好地控制其屏幕上显示的内容。
注意:为启动 Activity,系统仍会将运行前台服务的应用视为“后台”应用。
详情见BackgroundLaunchProcessController 介绍
// Allow if the flag was explicitly set.if(isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)){if(DEBUG_ACTIVITY_STARTS){Slog.d(TAG,"[Process("+ pid
+")] Activity start allowed: process allowed by token");}returntrue;}
应用在具有相同uid的现有Task中启动活动
// Do not allow background activity start in new task or in a task that uid is not present.// Also do not allow pinned window to start single instance activity in background,// as it will recreate the window and makes it to foreground.boolean blockBalInTask =(newTask
||!targetTask.isUidPresent(mCallingUid)||(LAUNCH_SINGLE_INSTANCE== mLaunchMode && targetTask.inPinnedWindowingMode()));if(mRestrictedBgActivity && blockBalInTask &&handleBackgroundActivityAbort(r)){Slog.e(TAG,"Abort background activity starts from "+ mCallingUid);returnSTART_ABORTED;}
如何申请豁免
权限相关
申请START_ACTIVITIES_FROM_BACKGROUND 权限
特权system/priv-app/目录下的app才可以申请豁免
<uses-permissionandroid:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<permissions><privapp-permissionspackage="com.android.xxx"><permissionname="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/></privapp-permissions></permissions>
申请SYSTEM_ALERT_WINDOW权限
注意:在 Android 10(Go 版本)上运行的应用无法获得SYSTEM_ALERT_WINDOW权限。
注意:如果应用程序以 API 级别 23 或更高级别为目标,则应用程序用户必须通过权限管理屏幕明确向应用程序授予此权限。
<uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Service有特殊client绑定
应用中的某个服务被另一个可见应用绑定
应用中的某个服务被另一个应用以BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag绑定
另一个应用需要声明START_ACTIVITIES_FROM_BACKGROUND权限
版权归原作者 AmyTan小小燕 所有, 如有侵权,请联系我们删除。