Android Q版本之后无法从后台启动Activity的流程

1,199 阅读7分钟

还是先看官方文档: developer.android.google.cn/about/versi…

在官方文档的重大变更里面,介绍了一条隐私性调整声明:

Restrictions on starting activities from the background

Starting in Android 10, the system places restrictions on starting activities from the background. This behavior change helps minimize interruptions for the user and keeps the user more in control of what's shown on their screen. As long as your app starts activities as a direct result of user interaction, your app most likely isn't affected by these restrictions.

To learn more about the recommended alternative to starting activities from the background, see the guide on how to alert users of time-sensitive events in your app.

大概意思就是说增加了对后台应用启动Activity的限制,让使用者能更好的决定自己的屏幕应该显示什么内容。这样主要考虑的还是安全性,当然一定程度上也会优化系统的performance。比如:

  1. 用户在看视频或玩游戏时不会被其他弹窗打断
  2. 避免流氓软件的弹窗

那么对于开发者来说,如何判断你的Activity被AMS挡掉了?可以搜索下这段log Background activity start 这段log是默认打印的,Activity的启动过程就不再介绍了,有很多Activity生命周期的文档可以参考。 /frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

939    boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
940            final String callingPackage, int realCallingUid, int realCallingPid,
941            WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
942            boolean allowBackgroundActivityStart, Intent intent) {
943        // don't abort for the most important UIDs
944        final int callingAppId = UserHandle.getAppId(callingUid);
945        if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
946                || callingAppId == Process.NFC_UID) {
947            return false;
948        }
949        // don't abort if the callingUid has a visible window or is a persistent system process
950        final int callingUidProcState = mService.getUidState(callingUid);
951        final boolean callingUidHasAnyVisibleWindow =
952                mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
953        final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
954                || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
955                || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
956        final boolean isCallingUidPersistentSystemProcess =
957                callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
958        if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
959            return false;
960        }

1044        // anything that has fallen through would currently be aborted
1045        Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
1046                + "; callingUid: " + callingUid
1047                + "; isCallingUidForeground: " + isCallingUidForeground
1048                + "; isCallingUidPersistentSystemProcess: " + isCallingUidPersistentSystemProcess
1049                + "; realCallingUid: " + realCallingUid
1050                + "; isRealCallingUidForeground: " + isRealCallingUidForeground
1051                + "; isRealCallingUidPersistentSystemProcess: "
1052                + isRealCallingUidPersistentSystemProcess
1053                + "; originatingPendingIntent: " + originatingPendingIntent
1054                + "; isBgStartWhitelisted: " + allowBackgroundActivityStart
1055                + "; intent: " + intent
1056                + "; callerApp: " + callerApp
1057                + "]");

这段代码很长,我们只需要知道,当你的Activity从后台启动被挡掉之后,是一定会打印这段log出来的。想要研究shouldAbortBackgroundActivityStart的流程可以再去添加callback,这个都很简单。我们要关注的是这段log中传达的信息

  1. callingPackage:调用此Activity的包名
  2. isCallingUidForeground:是否为前台用户组调用的
  3. isBgStartWhitelisted:是否在允许从后台启动的白名单中

其中最关键的就是第三个位置,isBgStartWhitelisted,我们可以直接从log中看到这里是true or false,很显然这里打印的是allowBackgroundActivityStart的布尔值,很显然如果这里打印false,说明这个activity是不允许从后台启动的。这个是使用startActivity的过程。那如果调用startActivities来批量启动Activity呢? /frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

1014    @Override
1015    public final int startActivities(IApplicationThread caller, String callingPackage,
1016            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
1017            int userId) {
1018        final String reason = "startActivities";
1019        enforceNotIsolatedCaller(reason);
1020        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
1021        // TODO: Switch to user app stacks here.
1022        return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
1023                intents, resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId,
1024                reason, null /* originatingPendingIntent */,
1025                false /* allowBackgroundActivityStart */);
1026    }

不用管多余的流程,直接看关键点,在ActivityTaskManagerService中,实际启动Activity时,是通过ActivityStartController来一次性启动多个activity的,这里直接僵硬的把代表allowBackgroundActivityStart的位置写入了false,在批量启动activity时,不允许从后台启动activity。

对于其他一些特殊Activity的启动,有时候会根据启动方式设置allowBackgroundActivityStart为true,也就是直接在构建ActivityStarter的时候调用 setAllowBackgroundActivityStart(true)直接写入一个true。

2908    ActivityStarter setAllowBackgroundActivityStart(boolean allowBackgroundActivityStart) {
2909        mRequest.allowBackgroundActivityStart = allowBackgroundActivityStart;
2910        return this;
2911    }
2910      

当然这个对应用层是不开放的,只是在ATMS中启动某些特殊的activity时会添加这个值。

最终是否允许从后台启动Activity都会被传入到 /frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java

266    final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
267            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
268            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
269            int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
270            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
271
272        userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
273                reason);
274
275        // TODO: Switch to user app stacks here.
276        return obtainStarter(intent, reason)
277                .setCallingUid(uid)
278                .setRealCallingPid(realCallingPid)
279                .setRealCallingUid(realCallingUid)
280                .setCallingPackage(callingPackage)
281                .setResolvedType(resolvedType)
282                .setResultTo(resultTo)
283                .setResultWho(resultWho)
284                .setRequestCode(requestCode)
285                .setStartFlags(startFlags)
286                .setActivityOptions(options)
287                .setMayWait(userId)
288                .setInTask(inTask)
289                .setOriginatingPendingIntent(originatingPendingIntent)
290                .setAllowBackgroundActivityStart(allowBackgroundActivityStart)
291                .execute();
292    }

在Anroid 10中到这里就结束了,Android 11中对什么样的Activity允许从后台启动,做了更详细的解释,参考这个函数shouldAbortBackgroundActivityStart /frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

1234    boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
1235            final String callingPackage, int realCallingUid, int realCallingPid,
1236            WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
1237            boolean allowBackgroundActivityStart, Intent intent) {
1238        // don't abort for the most important UIDs
1239        final int callingAppId = UserHandle.getAppId(callingUid);
1240        if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
1241                || callingAppId == Process.NFC_UID) {
1242            if (DEBUG_ACTIVITY_STARTS) {
1243                Slog.d(TAG, "Activity start allowed for important callingUid (" + callingUid + ")");
1244            }
1245            return false;
1246        }
1247
1248        // don't abort if the callingUid has a visible window or is a persistent system process
1249        final int callingUidProcState = mService.getUidState(callingUid);
1250        final boolean callingUidHasAnyVisibleWindow =
1251                mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid);
1252        final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow
1253                || callingUidProcState == ActivityManager.PROCESS_STATE_TOP
1254                || callingUidProcState == ActivityManager.PROCESS_STATE_BOUND_TOP;
1255        final boolean isCallingUidPersistentSystemProcess =
1256                callingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI;
1257        if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
1258            if (DEBUG_ACTIVITY_STARTS) {
1259                Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid
1260                        + ", isCallingUidPersistentSystemProcess = "
1261                        + isCallingUidPersistentSystemProcess);
1262            }
1263            return false;
1264        }
1265        // take realCallingUid into consideration
1266        final int realCallingUidProcState = (callingUid == realCallingUid)
1267                ? callingUidProcState
1268                : mService.getUidState(realCallingUid);
1269        final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
1270                ? callingUidHasAnyVisibleWindow
1271                : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid);
1272        final boolean isRealCallingUidForeground = (callingUid == realCallingUid)
1273                ? isCallingUidForeground
1274                : realCallingUidHasAnyVisibleWindow
1275                        || realCallingUidProcState == ActivityManager.PROCESS_STATE_TOP;
1276        final int realCallingAppId = UserHandle.getAppId(realCallingUid);
1277        final boolean isRealCallingUidPersistentSystemProcess = (callingUid == realCallingUid)

代码太长就不贴上来了,这个api可以去研究一下返回值,如果这里返回true表示拦截此启动过程,返回false表示不拦截,根据判断条件简单筛查了一下,几个比较常见的允许从后台启动Activity的情况

  1. 如果是系统里比较重要的用户组发出来的请求,允许从后台启动Activity
1238        // don't abort for the most important UIDs
1239        final int callingAppId = UserHandle.getAppId(callingUid);
1240        if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
1241                || callingAppId == Process.NFC_UID) {
1242            if (DEBUG_ACTIVITY_STARTS) {
1243                Slog.d(TAG, "Activity start allowed for important callingUid (" + callingUid + ")");
1244            }
1245            return false;
1246        }
  1. 当前环形Activity的进程在前台拥有非dialog类型的可见窗口,或属于系统进程
1257        if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
1258            if (DEBUG_ACTIVITY_STARTS) {
1259                Slog.d(TAG, "Activity start allowed: callingUidHasAnyVisibleWindow = " + callingUid
1260                        + ", isCallingUidPersistentSystemProcess = "
1261                        + isCallingUidPersistentSystemProcess);
1262            }
1263            return false;
1264        }
  1. 在白名单内
1291            // wasn't whitelisted to start an activity
1292            if (isRealCallingUidPersistentSystemProcess && allowBackgroundActivityStart) {
1293                if (DEBUG_ACTIVITY_STARTS) {
1294                    Slog.d(TAG, "Activity start allowed: realCallingUid (" + realCallingUid
1295                            + ") is persistent system process AND intent sender whitelisted "
1296                            + "(allowBackgroundActivityStart = true)");
1297                }
1298                return false;
1299            }

  1. 拥有 START_ACTIVITIES_FROM_BACKGROUND 权限
1310        // don't abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
1311        if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
1312                == PERMISSION_GRANTED) {
1313            if (DEBUG_ACTIVITY_STARTS) {
1314                Slog.d(TAG,
1315                        "Background activity start allowed: START_ACTIVITIES_FROM_BACKGROUND "
1316                                + "permission granted for uid "
1317                                + callingUid);
1318            }
1319            return false;
1320        }

还有其他一些返回条件,不过相对少见一点。清楚后台启动Activity为何会被挡掉之后,解决此问题就容易了,如果我们只是为了debug某些问题,可以这样修改 /frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

5404    boolean isBackgroundActivityStartsEnabled() {
5405        //return mAmInternal.isBackgroundActivityStartsEnabled();
            return true;
5406    }

这里直接return true,试下Activity现在是否可以正常启动了,如果确定Activity无法启动时因为被background start限制了,可以为APP添加 START_ACTIVITIES_FROM_BACKGROUND权限,这部分就与AMS无关了,具体可以参考Android permission相关资料。如果是厂商自己的apk,还可以直接把自己加到白名单。