android 11 background startup service cannot access camera (camera) to solve the problem

Android 11 background startup service cannot access camera (camera) solution

Android 11 has added permission to start a service in the background and cannot access the camera.

Solution

Solutions fall into two categories: 1 The initiator has foreground activity; 2 the initiator itself is a background service.

Initiator has foreground activity

Add the label of foreground service in the service to be started, indicating the need to use camera. This is relatively simple. Paste the code directly.

     <service
            android:name="xxxxxxx"
            android:foregroundServiceType="camera|location"
            android:exported="true">
            <intent-filter>
                <action android:name="xxxxxxxx" />
            </intent-filter>
        </service>

The initiator itself is a background service

This is the focus of this paper. It should be noted that if the initiator's process has foreground activity, it belongs to the first case. This article mainly refers to a service that starts the service of another app to use the camera. Why do you use it like this? Let's just discuss how to use it.

Call app1: Service - > app 2: Service - > camera
Here, even if app2 service is marked with the first one, the system is not allowed to call. The tips are:

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

This restriction logic is in active services. We analyze the reasons in detail.
First look at the limit points:

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

The main attribute is this attribute, allowwileinusepermissioninfgs. Where is this assigned? It's also in this class.

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

Find a solution for the assignment code.

  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) {//Can't go
            return true;
        }
        // Is the allow activity background start flag on?
        if (allowBackgroundActivityStarts) {//The parameter passed is 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) {//We are not system users
            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) {//White list not considered
            return true;
        }

        // Is the calling UID a device owner app?
        final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
        if (isDeviceOwner) {//Equipment owners, let alone
            return true;
        }
        return false;
    }

Step 1: add START_ACTIVITIES_FROM_BACKGROUND

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

Add it in and try. Does your app have any? No, keep looking down.

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

Whether app1 is a top process. Here is the definition of 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 is 2. For the first few, our app can't reach it.
It's easy to do this. You're the activity at the front desk. Therefore, this step does not work in our hypothetical environment.
PS. It can also be seen here that Android process priority control.

Continue:

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

Go in and look at the code. Don't be misled by comments. The code implementation is here: 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. Do you have an idea?
overlay dialog.
Is it OK?

Keywords: Android

Added by jrbissell on Tue, 08 Mar 2022 00:16:16 +0200