android 11 后台启动service不能访问camera(摄像头)解决

Android 11 后台启动service不能访问camera(摄像头)解决

Android 11增加了权限,在后台启动一个service,无法访问摄像头。

解决方案

解决方案分为两类:1.启动者有前台activity;2启动者本身是一个后台服务。

启动者有前台activity

在需要启动的服务里面增加前台服务的标签,注明需要使用camera。这个比较简单,直接贴代码。

     <service
            android:name="xxxxxxx"
            android:foregroundServiceType="camera|location"
            android:exported="true">
            <intent-filter>
                <action android:name="xxxxxxxx" />
            </intent-filter>
        </service>
启动者本身是一个后台服务

这个是本文解决的重点。需要说明的是,如果启动者所在的进程,有前台activity,那么属于第一种情况。本文主要是说的是,一个service,启动另外一个app的service来使用摄像头。为啥这么使用,先不管,我们只讨论怎么可以使用。

正常这样调用:app1 :service ->app 2:service->camera.
这里就算,app2 service,标注了第一种那样,系统也是不允许调用的。提示是:

Foreground service started from background can not havel ocation/camera/microphone access: service xxxxx

这个限制逻辑是在:ActiveServices里面。我们具体分析原因。
先看限制点:

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

主要就是这个属性mAllowWhileInUsePermissionInFgs,这个是在哪里赋值的呢?也是在这个类里面。

 s.mAllowWhileInUsePermissionInFgs =
                        shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
                                Binder.getCallingPid(), Binder.getCallingUid(),
                                service, s, false);
                                

针对赋值代码,找到解决方案。

  private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
            int callingPid, int callingUid, Intent intent, ServiceRecord r,
            boolean allowBackgroundActivityStarts) {
        // Is the background FGS start restriction turned on?
        if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {//不会走
            return true;
        }
        // Is the allow activity background start flag on?
        if (allowBackgroundActivityStarts) {//传参就是false
            return true;
        }

        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) {//我们也不是系统用户
            return true;
        }

        if (r.app != null) {//   // Whether the caller holds START_ACTIVITIES_FROM_BACKGROUND permission
            ActiveInstrumentation instr = r.app.getActiveInstrumentation();
            if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
                return true;
            }
        }

        final boolean hasAllowBackgroundActivityStartsToken = r.app != null
                ? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false;
        if (hasAllowBackgroundActivityStartsToken) {
            return true;
        }

        if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
                == PERMISSION_GRANTED) {
            return true;
        }

        // Is the calling UID at PROCESS_STATE_TOP or above?
        final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
        if (isCallingUidTopApp) {
            return true;
        }
        // Does the calling UID have any visible activity?
        final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
        if (isCallingUidVisible) {
            return true;
        }

        final boolean isWhiteListedPackage =
                mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage);
        if (isWhiteListedPackage) {//白名单不考虑
            return true;
        }

        // Is the calling UID a device owner app?
        final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
        if (isDeviceOwner) {//设备拥有者,更加不考虑
            return true;
        }
        return false;
    }

第一步解决:增加START_ACTIVITIES_FROM_BACKGROUND

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

加进去,试下,你的app是否有?没有,继续往下看。

 final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
        if (isCallingUidTopApp) {
            return true;
        }

app1 是否是top process。这里说下process state的定义:

@IntDef(flag = false, prefix = { "PROCESS_STATE_" }, value = {
        PROCESS_STATE_UNKNOWN, // -1
        PROCESS_STATE_PERSISTENT, // 0
        PROCESS_STATE_PERSISTENT_UI,
        PROCESS_STATE_TOP,
        PROCESS_STATE_BOUND_TOP,
        PROCESS_STATE_FOREGROUND_SERVICE,
        PROCESS_STATE_BOUND_FOREGROUND_SERVICE,
        PROCESS_STATE_IMPORTANT_FOREGROUND,
        PROCESS_STATE_IMPORTANT_BACKGROUND,
        PROCESS_STATE_TRANSIENT_BACKGROUND,
        PROCESS_STATE_BACKUP,
        PROCESS_STATE_SERVICE,
        PROCESS_STATE_RECEIVER,
        PROCESS_STATE_TOP_SLEEPING,
        PROCESS_STATE_HEAVY_WEIGHT,
        PROCESS_STATE_HOME,
        PROCESS_STATE_LAST_ACTIVITY,
        PROCESS_STATE_CACHED_ACTIVITY,
        PROCESS_STATE_CACHED_ACTIVITY_CLIENT,
        PROCESS_STATE_CACHED_RECENT,
        PROCESS_STATE_CACHED_EMPTY,
    })

PROCESS_STATE_TOP是2,前面几个,我们的app 基本达不到。
这个要做到,很简单,你是最前台的那个activity就是了。所以这一步,在我们假定的环境也不通。
PS。这里也可以看到,Android 进程优先级的控制。

继续往下:

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

进去看代码,不要被注释误导,代码实现是在:ActivityTaskManagerService这里。

boolean isUidForeground(int uid) {
        // A uid is considered to be foreground if it has a visible non-toast window.
        return mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid);
    }

看这句的注释,if it has a visible non-toast window。是不是就有想法了?
overlay dialog 来一把。
是不是就可以了?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值