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

在这里插入图片描述

限制后台启动activity

如果未满足相关条件,则后台不允许启动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才可以申请豁免

<!-- @SystemApi @hide Allows an application to start 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 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;
            }
应用是Recent组件所在uid下进程(这里一般为home进程)
        if (useCallingUidState) {
        	.......
            // 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;
            }
应用是设备所有者

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

        if (useCallingUidState) {
          ......
            // 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;
            }
应用通过 CompanionDeviceManager API 与配套硬件设备相关联。

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

        if (useCallingUidState) {
           ......
            // 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;
            }
用户已向应用授予 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 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;
            }
        }

关于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 process
    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);
            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;
        }

如何申请豁免

权限相关

申请START_ACTIVITIES_FROM_BACKGROUND 权限

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

<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
<permissions>
    <privapp-permissions package="com.android.xxx">
        <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
     </privapp-permissions>
</permissions>
申请SYSTEM_ALERT_WINDOW权限

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

 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Service有特殊client绑定

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

另一个应用需要声明START_ACTIVITIES_FROM_BACKGROUND权限

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值