Loading packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt +6 −21 Original line number Diff line number Diff line Loading @@ -34,8 +34,8 @@ import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnail import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider import com.android.systemui.settings.UserTracker import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.policy.ConfigurationController import dagger.Binds Loading @@ -54,13 +54,12 @@ import kotlinx.coroutines.SupervisorJob @Qualifier @Retention(AnnotationRetention.BINARY) annotation class HostUserHandle @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile @Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile @Retention(AnnotationRetention.RUNTIME) @Scope annotation class MediaProjectionAppSelectorScope @Module(subcomponents = [MediaProjectionAppSelectorComponent::class]) @Module( subcomponents = [MediaProjectionAppSelectorComponent::class], includes = [MediaProjectionDevicePolicyModule::class] ) interface MediaProjectionModule { @Binds @IntoMap Loading Loading @@ -109,20 +108,6 @@ interface MediaProjectionAppSelectorModule { activity: MediaProjectionAppSelectorActivity ): ConfigurationController = ConfigurationControllerImpl(activity) @Provides @PersonalProfile @MediaProjectionAppSelectorScope fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle { // Current foreground user is the 'personal' profile return UserHandle.of(activityManagerWrapper.currentUserId) } @Provides @WorkProfile @MediaProjectionAppSelectorScope fun workProfileUserHandle(userTracker: UserTracker): UserHandle? = userTracker.userProfiles.find { it.isManagedProfile }?.userHandle @Provides @HostUserHandle @MediaProjectionAppSelectorScope Loading packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * 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.appselector import android.content.Context import android.os.UserHandle import com.android.internal.R as AndroidR import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider import com.android.internal.app.ResolverListAdapter import com.android.systemui.R import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver import javax.inject.Inject @MediaProjectionAppSelectorScope class MediaProjectionBlockerEmptyStateProvider @Inject constructor( @HostUserHandle private val hostAppHandle: UserHandle, @PersonalProfile private val personalProfileHandle: UserHandle, private val policyResolver: ScreenCaptureDevicePolicyResolver, private val context: Context ) : EmptyStateProvider { override fun getEmptyState(resolverListAdapter: ResolverListAdapter): EmptyState? { val screenCaptureAllowed = policyResolver.isScreenCaptureAllowed( targetAppUserHandle = resolverListAdapter.userHandle, hostAppUserHandle = hostAppHandle ) val isHostAppInPersonalProfile = hostAppHandle == personalProfileHandle val subtitle = if (isHostAppInPersonalProfile) { AndroidR.string.resolver_cant_share_with_personal_apps_explanation } else { AndroidR.string.resolver_cant_share_with_work_apps_explanation } if (!screenCaptureAllowed) { return object : EmptyState { override fun getSubtitle(): String = context.resources.getString(subtitle) override fun getTitle(): String = context.resources.getString( R.string.screen_capturing_disabled_by_policy_dialog_title ) override fun onEmptyStateShown() { // TODO(b/237397740) report analytics } override fun shouldSkipDataRebuild(): Boolean = true } } return null } } packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/MediaProjectionDevicePolicyModule.kt 0 → 100644 +43 −0 Original line number Diff line number Diff line /* * 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.os.UserHandle import com.android.systemui.settings.UserTracker import com.android.systemui.shared.system.ActivityManagerWrapper import dagger.Module import dagger.Provides import javax.inject.Qualifier @Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile /** Module for media projection device policy related dependencies */ @Module class MediaProjectionDevicePolicyModule { @Provides @PersonalProfile fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle { // Current foreground user is the 'personal' profile return UserHandle.of(activityManagerWrapper.currentUserId) } @Provides @WorkProfile fun workProfileUserHandle(userTracker: UserTracker): UserHandle? = userTracker.userProfiles.find { it.isManagedProfile }?.userHandle } packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * 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 (!hostAppUserHandle.isWorkProfile() && 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 } packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * 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.content.Context import com.android.systemui.R import com.android.systemui.statusbar.phone.SystemUIDialog /** Dialog that shows that screen capture is disabled on this device. */ class ScreenCaptureDisabledDialog(context: Context) : SystemUIDialog(context) { init { setTitle(context.getString(R.string.screen_capturing_disabled_by_policy_dialog_title)) setMessage( context.getString(R.string.screen_capturing_disabled_by_policy_dialog_description) ) setIcon(R.drawable.ic_cast) setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> cancel() } } } Loading
packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt +6 −21 Original line number Diff line number Diff line Loading @@ -34,8 +34,8 @@ import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnail import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider import com.android.systemui.settings.UserTracker import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.policy.ConfigurationController import dagger.Binds Loading @@ -54,13 +54,12 @@ import kotlinx.coroutines.SupervisorJob @Qualifier @Retention(AnnotationRetention.BINARY) annotation class HostUserHandle @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile @Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile @Retention(AnnotationRetention.RUNTIME) @Scope annotation class MediaProjectionAppSelectorScope @Module(subcomponents = [MediaProjectionAppSelectorComponent::class]) @Module( subcomponents = [MediaProjectionAppSelectorComponent::class], includes = [MediaProjectionDevicePolicyModule::class] ) interface MediaProjectionModule { @Binds @IntoMap Loading Loading @@ -109,20 +108,6 @@ interface MediaProjectionAppSelectorModule { activity: MediaProjectionAppSelectorActivity ): ConfigurationController = ConfigurationControllerImpl(activity) @Provides @PersonalProfile @MediaProjectionAppSelectorScope fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle { // Current foreground user is the 'personal' profile return UserHandle.of(activityManagerWrapper.currentUserId) } @Provides @WorkProfile @MediaProjectionAppSelectorScope fun workProfileUserHandle(userTracker: UserTracker): UserHandle? = userTracker.userProfiles.find { it.isManagedProfile }?.userHandle @Provides @HostUserHandle @MediaProjectionAppSelectorScope Loading
packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionBlockerEmptyStateProvider.kt 0 → 100644 +70 −0 Original line number Diff line number Diff line /* * 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.appselector import android.content.Context import android.os.UserHandle import com.android.internal.R as AndroidR import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyState import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider import com.android.internal.app.ResolverListAdapter import com.android.systemui.R import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver import javax.inject.Inject @MediaProjectionAppSelectorScope class MediaProjectionBlockerEmptyStateProvider @Inject constructor( @HostUserHandle private val hostAppHandle: UserHandle, @PersonalProfile private val personalProfileHandle: UserHandle, private val policyResolver: ScreenCaptureDevicePolicyResolver, private val context: Context ) : EmptyStateProvider { override fun getEmptyState(resolverListAdapter: ResolverListAdapter): EmptyState? { val screenCaptureAllowed = policyResolver.isScreenCaptureAllowed( targetAppUserHandle = resolverListAdapter.userHandle, hostAppUserHandle = hostAppHandle ) val isHostAppInPersonalProfile = hostAppHandle == personalProfileHandle val subtitle = if (isHostAppInPersonalProfile) { AndroidR.string.resolver_cant_share_with_personal_apps_explanation } else { AndroidR.string.resolver_cant_share_with_work_apps_explanation } if (!screenCaptureAllowed) { return object : EmptyState { override fun getSubtitle(): String = context.resources.getString(subtitle) override fun getTitle(): String = context.resources.getString( R.string.screen_capturing_disabled_by_policy_dialog_title ) override fun onEmptyStateShown() { // TODO(b/237397740) report analytics } override fun shouldSkipDataRebuild(): Boolean = true } } return null } }
packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/MediaProjectionDevicePolicyModule.kt 0 → 100644 +43 −0 Original line number Diff line number Diff line /* * 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.os.UserHandle import com.android.systemui.settings.UserTracker import com.android.systemui.shared.system.ActivityManagerWrapper import dagger.Module import dagger.Provides import javax.inject.Qualifier @Qualifier @Retention(AnnotationRetention.BINARY) annotation class WorkProfile @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PersonalProfile /** Module for media projection device policy related dependencies */ @Module class MediaProjectionDevicePolicyModule { @Provides @PersonalProfile fun personalUserHandle(activityManagerWrapper: ActivityManagerWrapper): UserHandle { // Current foreground user is the 'personal' profile return UserHandle.of(activityManagerWrapper.currentUserId) } @Provides @WorkProfile fun workProfileUserHandle(userTracker: UserTracker): UserHandle? = userTracker.userProfiles.find { it.isManagedProfile }?.userHandle }
packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * 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 (!hostAppUserHandle.isWorkProfile() && 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 }
packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDisabledDialog.kt 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * 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.content.Context import com.android.systemui.R import com.android.systemui.statusbar.phone.SystemUIDialog /** Dialog that shows that screen capture is disabled on this device. */ class ScreenCaptureDisabledDialog(context: Context) : SystemUIDialog(context) { init { setTitle(context.getString(R.string.screen_capturing_disabled_by_policy_dialog_title)) setMessage( context.getString(R.string.screen_capturing_disabled_by_policy_dialog_description) ) setIcon(R.drawable.ic_cast) setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok)) { _, _ -> cancel() } } }