0


android 10+从后台启动 Activity 的限制

限制后台启动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权限


本文转载自: https://blog.csdn.net/xiaoyantan/article/details/128401740
版权归原作者 AmyTan小小燕 所有, 如有侵权,请联系我们删除。

“android 10+从后台启动 Activity 的限制”的评论:

还没有评论