Android 10+ restrictions on starting Activity from the background

Restrict background start activity

If the relevant conditions are not met, the activity is not allowed to start in the background, and the following related logs will be printed:

// 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())
       + "]");

Exceptions to restrictions

Apps running on Android 10 or higher can only start an activity if one or more of the following conditions are met:

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 process

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

The process under the uid where the Ime window is located

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

The application has a visible window, such as a foreground Activity

The application is a persistent system process and is performing UI operations

// 是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;
}

App declares START_ACTIVITIES_FROM_BACKGROUND permission

Only apps under the privileged system/priv-app/ directory can apply for exemption

<!-- @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;
            }

The application is the process under the uid of the Recent component (here is generally the home process)

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

App is the device owner

Apps are device policy controllers that run in device owner mode . Example use cases include fully managed enterprise devices , as well as specialized devices such as digital signage and kiosks .

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

Applications are associated with companion hardware devices through the CompanionDeviceManager API.

This API supports the app launch API in response to user actions on the paired device.

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

User has granted SYSTEM_ALERT_WINDOW permission to app

**Note:** Apps running on Android 10 (Go version) cannot get the SYSTEM_ALERT_WINDOW permission .
Note: If the application targets API level 23 or higher, the application user must explicitly grant this permission to the application through the permission management screen.

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

About the areBackgroundActivityStartsAllowed series

If we don't have a callerApp at this point, no caller is provided to startActivity(). This is the case for PendingIntent-based launches, since the creator's process may not be started and be active. If this is the case, we retrieve the WindowProcessController for the caller of send() if the caller allows it so we can make a decision based on its state.

// 遗留行为允许使用调用者前台状态绕过 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;
            }
        }
    }
}

An activity of the application was started/ended a while ago

If app switching is not allowed, we ignore all launch activity grace period exceptions, so the app cannot launch itself in onPause() after pressing the home button.

The Activity can be started in the background within 10s of starting or finishing the activity after the time of stopping the application switching

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

            }
        }

The process of Active Instrumentation with the privilege of starting activity in the background

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

The application owns the Activity in the back stack of the foreground task

android U may be canceled

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

A service in the application is bound by another visible application

Note that the app bound to the service must remain visible for the background app to successfully launch the 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);
    }

The application receives the PendingIntent notification from the system

For pending intents for services and broadcast receivers, apps can start an activity a few seconds after the pending intent is sent

The app receives a PendingIntent from another visible app

The application's Service has a connection with BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS

Need to declare START_ACTIVITIES_FROM_BACKGROUND permission

Android 10 (API level 29) and higher imposes limits on how long a background app can start an Activity . These limits help minimize disruption to users and give users more control over what appears on their screens.
**NOTE:** For the purpose of starting an Activity, the system still considers an app running a foreground service to be a "background" app.

See the introduction of BackgroundLaunchProcessController for details

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

App starts the activity in an existing Task with the same uid

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

How to apply for a waiver

Permission related

Request START_ACTIVITIES_FROM_BACKGROUND permission

Only apps under the privileged system/priv-app/ directory can apply for exemption

<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>

Apply for SYSTEM_ALERT_WINDOW permission

**Note:** Apps running on Android 10 (Go version) cannot get the SYSTEM_ALERT_WINDOW permission .
Note: If the application targets API level 23 or higher, the application user must explicitly grant this permission to the application through the permission management screen.

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

Service has a special client binding

A service in the application is bound by another visible application

A service in the application is bound by another application with the BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS flag

Another application needs to declare the START_ACTIVITIES_FROM_BACKGROUND permission

Guess you like

Origin blog.csdn.net/xiaoyantan/article/details/128401740
Recommended