从后台启动 Activity 的限制
admin
2024-04-30 17:42:52
0

限制后台启动activity

android 10+ 如果未满足相关条件,则后台不允许启动activity,并会打印如下相关的log:

// anything that has fallen through would currently be aborted
Slog.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

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;}......

home 进程

        if (useCallingUidState) {......if (isHomeApp(callingUid, callingPackage)) {if (DEBUG_ACTIVITY_STARTS) {Slog.d(TAG,"Activity start allowed for home app callingUid (" + callingUid + ")");}return false;}

Ime窗口所在uid下的进程

        if (useCallingUidState) {......// 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;}

应用具有可见窗口,例如前台 Activity

应用是persistent系统进程,并正在执行UI操作

// 是persistent系统进程,并正在执行UI操作
final boolean isCallingUidPersistentSystemProcess =callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
// 如果允许应用程序切换,则允许具有可见应用程序窗口的普通应用程序启动活动,
// 或者将允许具有非应用程序可见窗口的动态壁纸等应用程序。
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;
}

应用声明了START_ACTIVITIES_FROM_BACKGROUND 权限

特权system/priv-app/目录下的app才可以申请豁免



        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);}return false;}

应用是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");}return false;}

应用是设备所有者

应用是在设备所有者模式下运行的设备政策控制器。示例用例包括完全托管的企业设备,以及数字标识牌和自助服务终端等专用设备。

        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");}return false;}

应用通过 CompanionDeviceManager API 与配套硬件设备相关联。

此 API 支持应用启动 API,以响应用户在配对设备上执行的操作。

        if (useCallingUidState) {......// don't abort if the callingUid has companion devicefinal 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;}

用户已向应用授予 SYSTEM_ALERT_WINDOW 权限

**注意:**在 Android 10(Go 版本)上运行的应用无法获得SYSTEM_ALERT_WINDOW权限。
注意:如果应用程序以 API 级别 23 或更高级别为目标,则应用程序用户必须通过权限管理屏幕明确向应用程序授予此权限。


        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.");return false;}}

关于areBackgroundActivityStartsAllowed系列

如果此时我们没有 callerApp,则没有向 startActivity() 提供调用者。 基于 PendingIntent 的启动就是这种情况,因为创建者的进程可能未启动且处于活动状态。 如果是这种情况,我们会在调用者允许的情况下为 send() 调用者检索 WindowProcessController,以便我们可以根据其状态做出决定。

// 遗留行为允许使用调用者前台状态绕过 BAL 限制。
final boolean 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");}return false;}// only if that one wasn't allowed, check the other onesfinal ArraySet uidProcesses =mService.mProcessMap.getProcesses(callerAppUid);if (uidProcesses != null) {for (int i = uidProcesses.size() - 1; i >= 0; i--) {final WindowProcessController 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");}return false;}}}
}

应用的某个 Activity 刚在不久前启动/结束

如果不允许应用程序切换,我们将忽略所有启动activity宽限期异常,因此应用程序无法在按下主页按钮后在 onPause() 中自行启动。

在停止应用程序切换的时间之后启动或finish activity的10s内可后台启动Activity

    boolean areBackgroundActivityStartsAllowed(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,则允许,final long 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");}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");}}}

具有后台启动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");}return true;}

应用在前台任务的返回栈中拥有 Activity

android U或许会取消

    boolean hasActivityInVisibleTask() {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");}return true;}

应用中的某个服务被另一个可见应用绑定

请注意,绑定到服务的应用必须保持可见,以便后台应用成功启动 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");}return true;}private boolean isBoundByForegroundUid() {synchronized (this) {if (mBoundClientUids != null) {for (int i = mBoundClientUids.size() - 1; i >= 0; i--) {if (mUidHasActiveVisibleWindowPredicate.test(mBoundClientUids.get(i))) {return true;}}}}return false;}
    /** A uid is considered to be foreground if it has a visible non-toast window. */@HotPath(caller = HotPath.START_SERVICE)boolean hasActiveVisibleWindow(int uid) {if (mVisibleActivityProcessTracker.hasVisibleActivity(uid)) {return true;}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");}return true;}

应用在具有相同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);return START_ABORTED;}

普通App如何获得允许

  • 申请 SYSTEM_ALERT_WINDOW 权限,且用户通过权限管理屏幕明确向应用程序授予此权限
  • 应用收到系统的 PendingIntent 通知
  • 应用收到另一个可见应用发送的 PendingIntent
  • 应用中的某个服务被另一个可见应用绑定

相关内容

热门资讯

夜间嘀打的声音,民间有传说这是... 夜间嘀打的声音,民间有传说这是巧媳妇儿要上门的说法是真的吗?夜间嘀嗒的声音来源多种,但传说只是只是一...
红小豆和赤小豆药用有什么区别 红小豆和赤小豆药用有什么区别两者颜色相同,但是形状不同。红豆比较圆,赤小尘御豆是细长的,稍扁。煮完之...
美的置业开启并购模式,逆势扩张... 美的置业开启并购模式,逆势扩张究竟意欲何为?美的它的产品非常受欢迎,所以公司盈利很大,借此机会正好可...
带智的成语有哪些成语 带智的成语有哪些成语大智若愚、见仁见智、利令智昏、流言止于智者、急中生智、智圆行方、绝圣弃智、足智多...
我和老公都想要孩子,怎么办? 我和老公都想要孩子,怎么办?不要孩子是一个人生大事,涉及到个人的生命周期、家庭规划和人生价值观等问题...
怎么写精彩软文 怎么写精彩软文一、寻找软文的新闻由头所谓新闻由头,指客观事实作为新闻传播的依据或契机,是一个事实所以...
真实 真实秃鹫又名座山雕、狗头鹫,分布在我国的新疆、青海、甘肃、宁夏、内蒙古等地。它们属于大型猛禽。全长约...
原创 中... 最近听说中国旅行团在肯尼亚被抢还有意大利丢东西的事真吓人。江苏那个21岁小姑娘拍视频说他们在肯尼亚村...
“孕早期请假数月不上班,老公是... 7月9日,据云南省红河县联合调查组通报: 近日,网传“红河县文旅局一员工自曝孕早期请假数月”,红河县...
比亚迪太会玩了,携手乐高乐园,... 想象一下,你家娃还在玩泥巴的年纪,就已经拿到了人生第一张"驾驶证"!这不是科幻片,而是比亚迪和上海乐...
世界著名人物顾拜旦 世界著名人物顾拜旦皮埃尔·德·顾拜旦(Le baron Pierre De Coubertin,18...
关于乐理知识书的事 关于乐理知识书的事乐理是最基本的~没有流行不流行的 你买李重光的~便宜还~9.8元 和声你要是流行的...
宇智波斑为什么被叫天启? 宇智波斑为什么被叫天启?柱扮中间说的,我觉得应厅稿山该出自敬大这句吧。
如果我戒掉了烟是否也能戒掉你`... 如果我戒掉了烟是否也能戒掉你``````求你想求什么?两码事!烟是可以戒的,爱情 情感怎么可能戒得掉...
说评书方法 如何把评书说好 说评书方法 如何把评书说好1、说功:所谓“说功”就是嘴上的功夫.说书人并非平铺直叙地照本宣读,它是需...
如何应用思维导图来进行阅读笔记... 如何应用思维导图来进行阅读笔记?猫博士用思维导图读绘本。怎样才能让宝宝读完绘本,看完热闹,还能有更多...
小说应重点描述什么 小说应重点描述什么小说应重点描述什么环境,对话,心理还是动作人物鲜明,情节有起伏波澜,吊人胃口,环境...
我要求奇志大兵的一段相声? 我要求奇志大兵的一段相声?有吗?奇、大有过这样的段子吗?笑星boy:)
怎样才能成为真正的投资大师呢 怎样才能成为真正的投资大师呢心态,先把心态锻炼好
儿子很多愁善感,看个动画片都哭... 儿子很多愁善感,看个动画片都哭,怎样引导? 小千千快四周岁了男孩哦听故事比如白雪公主第一句,白雪公主...