/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.mediaprojection.devicepolicy import android.app.admin.DevicePolicyManager import android.os.UserHandle import android.os.UserManager import javax.inject.Inject /** * Utility class to resolve if screen capture allowed for a particular target app/host app pair. It * caches the state of the policies, so you need to create a new instance of this class if you want * to react to updated policies state. */ class ScreenCaptureDevicePolicyResolver @Inject constructor( private val devicePolicyManager: DevicePolicyManager, private val userManager: UserManager, @PersonalProfile private val personalProfileUserHandle: UserHandle, @WorkProfile private val workProfileUserHandle: UserHandle? ) { /** * Returns true if [hostAppUserHandle] is allowed to perform screen capture of * [targetAppUserHandle] */ fun isScreenCaptureAllowed( targetAppUserHandle: UserHandle, hostAppUserHandle: UserHandle, ): Boolean { if (hostAppUserHandle.isWorkProfile() && workProfileScreenCaptureDisabled) { // Disable screen capturing as host apps should not capture the screen return false } if (personalProfileScreenCaptureDisabled) { // Disable screen capturing as personal apps should not capture the screen return false } if (targetAppUserHandle.isWorkProfile()) { // Work profile target if (workProfileScreenCaptureDisabled) { // Do not allow sharing work profile apps as work profile capturing is disabled return false } } else { // Personal profile target if (hostAppUserHandle.isWorkProfile() && disallowSharingIntoManagedProfile) { // Do not allow sharing of personal apps into work profile apps return false } if (personalProfileScreenCaptureDisabled) { // Disable screen capturing as personal apps should not be captured return false } } return true } /** * Returns true if [hostAppUserHandle] is NOT allowed to capture an app from any profile, * could be useful to finish the screen capture flow as soon as possible when the screen * could not be captured at all. */ fun isScreenCaptureCompletelyDisabled(hostAppUserHandle: UserHandle): Boolean { val isWorkAppsCaptureDisabled = if (workProfileUserHandle != null) { !isScreenCaptureAllowed( targetAppUserHandle = workProfileUserHandle, hostAppUserHandle = hostAppUserHandle ) } else true val isPersonalAppsCaptureDisabled = !isScreenCaptureAllowed( targetAppUserHandle = personalProfileUserHandle, hostAppUserHandle = hostAppUserHandle ) return isWorkAppsCaptureDisabled && isPersonalAppsCaptureDisabled } private val personalProfileScreenCaptureDisabled: Boolean by lazy { devicePolicyManager.getScreenCaptureDisabled( /* admin */ null, personalProfileUserHandle.identifier ) } private val workProfileScreenCaptureDisabled: Boolean by lazy { workProfileUserHandle?.let { devicePolicyManager.getScreenCaptureDisabled(/* admin */ null, it.identifier) } ?: false } private val disallowSharingIntoManagedProfile: Boolean by lazy { workProfileUserHandle?.let { userManager.hasUserRestrictionForUser( UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, it ) } ?: false } private fun UserHandle?.isWorkProfile(): Boolean = this == workProfileUserHandle }