android 11+后台启动FGS的while-in-use权限限制

while-in-use权限限制

为了帮助保护用户隐私,Android 11(API 级别 30)对前台服务何时可以访问设备的位置、摄像头或麦克风进行了限制。 当您的应用程序在后台运行时启动前台服务时,前台服务有以下限制:

  • 除非用户已向您的应用授予 ACCESS_BACKGROUND_LOCATION 权限,否则前台服务无法访问位置。
  • 前台服务无法访问麦克风或摄像头。
确定您的应用程序中哪些服务受到影响

测试您的应用程序时,启动其前台服务。 如果启动的服务限制了对位置、麦克风和摄像头的访问,Logcat 中会显示以下消息:
Foreground service started from background can not have location/camera/microphone access: service SERVICE_NAME

限制原理

FGS 有两个限制:
在 R 中,mAllowWhileInUsePermissionInFgs 是允许在前台服务中使用 while-in-use 权限。 从后台启动的 FGS 中的使用中权限可能会受到限制。
在S中,mAllowStartForeground是允许FGS是否startForeground。 从后台启动的服务可能不会成为 FGS。具体见后台启动FGS限制

启动或绑定或调用startForeground时会调用setFgsRestrictionLocked方法去校验上面的两个FGS限制。
在这里插入图片描述

private void setFgsRestrictionLocked(String callingPackage,
        int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId,
        boolean allowBackgroundActivityStarts) {
    	.......
    if (!r.mAllowWhileInUsePermissionInFgs
            || (r.mAllowStartForeground == REASON_DENIED)) {
        // while in use权限校验
        final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
                callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts);
        if (!r.mAllowWhileInUsePermissionInFgs) {
            r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
        }
        // 是否允许后台启动FGS校验
       .......
    }

如果前台服务不是从 TOP 进程启动的,则不允许它在使用中访问位置/相机/麦克风。

if (!r.mAllowWhileInUsePermissionInFgs) {
    Slog.w(TAG,
            "Foreground service started from background can not have "
                    + "location/camera/microphone access: service "
                    + r.shortInstanceName);
}

限制豁免

在某些情况下,即使应用程序在后台运行时启动了前台服务,它仍然可以在应用程序在前台运行时(“使用中”)访问位置、摄像头和麦克风信息。 在这些相同的情况下,如果该服务声明了前台服务类型的位置并由具有 ACCESS_BACKGROUND_LOCATION 权限的应用程序启动,则该服务可以一直访问位置信息,即使该应用程序在后台运行时也是如此。

是否应允许 FGS 中的使用中权限。 一个典型的 BG 启动的 FGS 不允许有 while-in-use 权限

该服务由前台应用启动

后台启动FGS限制-应用程序在前台 一样

  • REASON_PROC_STATE_PERSISTENT
  • REASON_PROC_STATE_PERSISTENT_UI
  • REASON_PROC_STATE_TOP
private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,int callingPid, int callingUid, @Nullable ServiceRecord targetService,
    boolean allowBackgroundActivityStarts) {
    int ret = REASON_DENIED;

    final int uidState = mAm.getUidStateLocked(callingUid);
    if (ret == REASON_DENIED) {
            // Is the calling UID at PROCESS_STATE_TOP or above?
        if (uidState <= PROCESS_STATE_TOP) {
            ret = getReasonCodeFromProcState(uidState);
        }
    }
该服务由可见应用启动

类似后台启动FGS限制-应用程序可见

  • REASON_UID_VISIBLE
        if (ret == REASON_DENIED) {
            // Does the calling UID have any visible activity?
            final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
            if (isCallingUidVisible) {
                ret = REASON_UID_VISIBLE;
            }
        }

该服务通过与通知交互来启动
该服务作为从不同的可见应用程序发送的 PendingIntent 启动

后台启动FGS限制-该服务通过与通知交互来启动

  • REASON_START_ACTIVITY_FLAG
        if (ret == REASON_DENIED) {
            // Is the allow activity background start flag on?
            if (allowBackgroundActivityStarts) {
                ret = REASON_START_ACTIVITY_FLAG;
            }
        }
该服务由系统组件启动(root/system/nfc/shell)
  • REASON_SYSTEM_UID

后台启动FGS限制-该服务由系统组件启动

        if (ret == REASON_DENIED) {
            boolean isCallerSystem = false;
            final int callingAppId = UserHandle.getAppId(callingUid);
            switch (callingAppId) {
                case ROOT_UID:
                case SYSTEM_UID:
                case NFC_UID:
                case SHELL_UID:
                    isCallerSystem = true;
                    break;
                default:
                    isCallerSystem = false;
                    break;
            }

            if (isCallerSystem) {
                ret = REASON_SYSTEM_UID;
            }
        }
该服务由具有 START_ACTIVITIES_FROM_BACKGROUND 特权权限的应用程序启动

后台启动FGS限制

  • REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION
  • REASON_BACKGROUND_ACTIVITY_PERMISSION
        if (ret == REASON_DENIED) {
            if (targetService != null && targetService.app != null) {
                ActiveInstrumentation instr = targetService.app.getActiveInstrumentation();
                if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
                    ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
                }
            }
        }
        if (ret == REASON_DENIED) {
            if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
                    == PERMISSION_GRANTED) {
                ret = REASON_BACKGROUND_ACTIVITY_PERMISSION;
            }
        }
该服务由系统Captions Service/Attention Service应用启动
  • REASON_ALLOWLISTED_PACKAGE
if (ret == REASON_DENIED) {
    if (verifyPackage(callingPackage, callingUid)) {
        final boolean isAllowedPackage =
                mAllowListWhileInUsePermissionInFgs.contains(callingPackage);
        if (isAllowedPackage) {
            ret = REASON_ALLOWLISTED_PACKAGE;
        }
    } else {
        EventLog.writeEvent(0x534e4554, "215003903", callingUid,
        	"callingPackage:" + callingPackage + " does not belong to callingUid:"
                        + callingUid);
    }
}
private void setAllowListWhileInUsePermissionInFgs() {
    final String attentionServicePackageName =
            mAm.mContext.getPackageManager().getAttentionServicePackageName();
    if (!TextUtils.isEmpty(attentionServicePackageName)) {
        mAllowListWhileInUsePermissionInFgs.add(attentionServicePackageName);
    }
    final String systemCaptionsServicePackageName =
            mAm.mContext.getPackageManager().getSystemCaptionsServicePackageName();
    if (!TextUtils.isEmpty(systemCaptionsServicePackageName)) {
        mAllowListWhileInUsePermissionInFgs.add(systemCaptionsServicePackageName);
    }
}
该服务由应用程序启动,该应用程序是在设备所有者模式下运行的设备策略控制器。
  • REASON_DEVICE_OWNER
        if (ret == REASON_DENIED) {
            // Is the calling UID a device owner app?
            final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
            if (isDeviceOwner) {
                ret = REASON_DEVICE_OWNER;
            }
        }
该服务由MediaSeesion相关的应用启动
        if (ret == REASON_DENIED) {
            if (mAm.mInternal.isTempAllowlistedForFgsWhileInUse(callingUid)) {
                return REASON_TEMP_ALLOWED_WHILE_IN_USE;
            }
        }
if (canAllowWhileInUse) {
    mActivityManagerLocal.tempAllowWhileInUsePermissionInFgs(targetUid,
            MediaSessionDeviceConfig
                    .getMediaSessionCallbackFgsWhileInUseTempAllowDurationMs());
}

关于areBackgroundActivityStartsAllowed系列

后台启动FGS限制

该服务由某个 Activity 刚在不久前启动/结束的应用启动
该服务由运行后台启动activity特权的Active Instrumentation的应用启动
该服务由在前台任务的返回栈中拥有 Activity的应用启动
该服务由某个服务被另一个可见应用绑定的应用启动

  • REASON_ACTIVITY_STARTER
        if (ret == REASON_DENIED) {
            final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, pr -> {
                if (pr.uid == callingUid) {
                    if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) {
                        return REASON_ACTIVITY_STARTER;
                    }
                }
                return null;
            });
            if (allowedType != null) {
                ret = allowedType;
            }
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值