From 5bcf61d134b86dda322056f503ac64dcae8c53fe Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Wed, 20 Mar 2024 21:13:57 -0400 Subject: Adds PolicyRequestProcessor to support new policy checks This request processor replaces the existing one. The interface remains the same but adds a new set of dependencies and support code (see previous commits). The goal is to provide an easy path for future policy rules and added capabilities. The first being a new check for special handling of private profile content. Bug: 327613051 Test: TODO Flag: ACONFIG com.android.systemui.screenshot_private_profile DEVELOPMENT Change-Id: I4f35a4551638f372331922153643e917fa46dee0 --- .../screenshot/ScreenshotRequestProcessor.kt | 7 +- .../systemui/screenshot/TakeScreenshotExecutor.kt | 16 +-- .../screenshot/data/model/ChildTaskModel.kt | 35 ++++++ .../screenshot/data/model/DisplayContentModel.kt | 2 +- .../data/repository/DisplayContentRepository.kt | 2 +- .../screenshot/policy/CaptureParameters.kt | 30 +++++ .../systemui/screenshot/policy/CapturePolicy.kt | 28 +++++ .../systemui/screenshot/policy/CaptureType.kt | 31 +++++ .../screenshot/policy/PolicyRequestProcessor.kt | 134 +++++++++++++++++++++ .../screenshot/policy/PrivateProfilePolicy.kt | 60 +++++++++ .../systemui/screenshot/policy/RootTaskInfoExt.kt | 47 ++++++++ .../screenshot/policy/ScreenshotPolicyModule.kt | 37 +++++- .../screenshot/policy/WorkProfilePolicy.kt | 56 +++++++++ 13 files changed, 467 insertions(+), 18 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/data/model/ChildTaskModel.kt create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt index 796457d88cc2..3ad4075a2b89 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt @@ -17,13 +17,14 @@ package com.android.systemui.screenshot /** Processes a screenshot request sent from [ScreenshotHelper]. */ -interface ScreenshotRequestProcessor { +fun interface ScreenshotRequestProcessor { /** * Inspects the incoming ScreenshotData, potentially modifying it based upon policy. * - * @param screenshot the screenshot to process + * @param original the screenshot to process + * @return a potentially modified screenshot data */ - suspend fun process(screenshot: ScreenshotData): ScreenshotData + suspend fun process(original: ScreenshotData): ScreenshotData } /** Exception thrown by [RequestProcessor] if something goes wrong. */ diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt index 92d3e550dc0f..ec7707c83980 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt @@ -92,14 +92,14 @@ constructor( // Let's wait before logging "screenshot requested", as we should log the processed // ScreenshotData. val screenshotData = - try { - screenshotRequestProcessor.process(rawScreenshotData) - } catch (e: RequestProcessorException) { - Log.e(TAG, "Failed to process screenshot request!", e) - logScreenshotRequested(rawScreenshotData) - onFailedScreenshotRequest(rawScreenshotData, callback) - return - } + runCatching { screenshotRequestProcessor.process(rawScreenshotData) } + .onFailure { + Log.e(TAG, "Failed to process screenshot request!", it) + logScreenshotRequested(rawScreenshotData) + onFailedScreenshotRequest(rawScreenshotData, callback) + } + .getOrNull() + ?: return logScreenshotRequested(screenshotData) Log.d(TAG, "Screenshot request: $screenshotData") diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/model/ChildTaskModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/ChildTaskModel.kt new file mode 100644 index 000000000000..c380db0ca3a4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/ChildTaskModel.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 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.screenshot.data.model + +import android.content.ComponentName +import android.graphics.Rect + +/** A child task within a RootTaskInfo */ +data class ChildTaskModel( + /** The task identifier */ + val id: Int, + /** The task name */ + val name: String, + /** The location and size of the task */ + val bounds: Rect, + /** The user which created the task. */ + val userId: Int, +) { + val componentName: ComponentName? + get() = ComponentName.unflattenFromString(name) +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt index 837a661230cb..2048b7c0c142 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/model/DisplayContentModel.kt @@ -24,6 +24,6 @@ data class DisplayContentModel( val displayId: Int, /** Information about the current System UI state which can affect capture. */ val systemUiState: SystemUiState, - /** A list of root tasks on the display, ordered from bottom to top along the z-axis */ + /** A list of root tasks on the display, ordered from top to bottom along the z-axis */ val rootTasks: List, ) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepository.kt b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepository.kt index 9c81b322a2b7..48e813d89af7 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/data/repository/DisplayContentRepository.kt @@ -18,7 +18,7 @@ package com.android.systemui.screenshot.data.repository import com.android.systemui.screenshot.data.model.DisplayContentModel /** Provides information about tasks related to a display. */ -interface DisplayContentRepository { +fun interface DisplayContentRepository { /** Provides information about the tasks and content presented on a given display. */ suspend fun getDisplayContent(displayId: Int): DisplayContentModel } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt new file mode 100644 index 000000000000..5e2b57651de7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureParameters.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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.screenshot.policy + +import android.content.ComponentName +import android.os.UserHandle + +/** The parameters dictated by a [CapturePolicy], used to adjust alter screenshot request. */ +data class CaptureParameters( + /** How should the content be captured? */ + val type: CaptureType, + /** The focused or top component at the time of the screenshot. */ + val component: ComponentName?, + /** Which user should receive the screenshot file? */ + val owner: UserHandle, +) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt new file mode 100644 index 000000000000..4a88180d8f73 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CapturePolicy.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 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.screenshot.policy + +import com.android.systemui.screenshot.data.model.DisplayContentModel + +/** Contains logic to determine when and how an adjust to screenshot behavior applies. */ +fun interface CapturePolicy { + /** + * Test the policy against the current display task state. If the policy applies, Returns a + * non-null [CaptureParameters] describing how the screenshot request should be augmented. + */ + suspend fun apply(content: DisplayContentModel): CaptureParameters? +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt new file mode 100644 index 000000000000..6ca2e9d6d5e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/CaptureType.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 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.screenshot.policy + +import android.graphics.Rect + +/** What to capture */ +sealed interface CaptureType { + /** Capture the entire screen contents. */ + class FullScreen(val displayId: Int) : CaptureType + + /** Capture the contents of the task only. */ + class IsolatedTask( + val taskId: Int, + val taskBounds: Rect?, + ) : CaptureType +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt new file mode 100644 index 000000000000..2c0a0dbf8ea9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 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.screenshot.policy + +import android.content.ComponentName +import android.graphics.Bitmap +import android.graphics.Rect +import android.os.UserHandle +import android.util.Log +import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN +import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.screenshot.ImageCapture +import com.android.systemui.screenshot.ScreenshotData +import com.android.systemui.screenshot.ScreenshotRequestProcessor +import com.android.systemui.screenshot.data.repository.DisplayContentRepository +import com.android.systemui.screenshot.policy.CaptureType.FullScreen +import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext + +private const val TAG = "PolicyRequestProcessor" + +/** A [ScreenshotRequestProcessor] which supports general policy rule matching. */ +class PolicyRequestProcessor( + @Background private val background: CoroutineDispatcher, + private val capture: ImageCapture, + private val displayTasks: DisplayContentRepository, + private val policies: List, +) : ScreenshotRequestProcessor { + override suspend fun process(original: ScreenshotData): ScreenshotData { + if (original.type == TAKE_SCREENSHOT_PROVIDED_IMAGE) { + // The request contains an already captured screenshot, accept it as is. + Log.i(TAG, "Screenshot bitmap provided. No modifications applied.") + return original + } + + val tasks = displayTasks.getDisplayContent(original.displayId) + + // If policies yield explicit modifications, apply them and return the result + Log.i(TAG, "Applying policy checks....") + policies + .firstNotNullOfOrNull { policy -> policy.apply(tasks) } + ?.let { + Log.i(TAG, "Modifying screenshot: $it") + return apply(it, original) + } + + // Otherwise capture normally, filling in additional information as needed. + return replaceWithScreenshot( + original = original, + componentName = original.topComponent ?: tasks.rootTasks.firstOrNull()?.topActivity, + owner = original.userHandle, + displayId = original.displayId + ) + } + + /** Produce a new [ScreenshotData] using [CaptureParameters] */ + suspend fun apply(updates: CaptureParameters, original: ScreenshotData): ScreenshotData { + // Update and apply bitmap capture depending on the parameters. + val updated = + when (val type = updates.type) { + is IsolatedTask -> + replaceWithTaskSnapshot( + original, + updates.component, + updates.owner, + type.taskId, + type.taskBounds + ) + is FullScreen -> + replaceWithScreenshot( + original, + updates.component, + updates.owner, + type.displayId + ) + } + return updated + } + + suspend fun replaceWithTaskSnapshot( + original: ScreenshotData, + componentName: ComponentName?, + owner: UserHandle, + taskId: Int, + taskBounds: Rect?, + ): ScreenshotData { + val taskSnapshot = capture.captureTask(taskId) + return original.copy( + type = TAKE_SCREENSHOT_PROVIDED_IMAGE, + bitmap = taskSnapshot, + userHandle = owner, + taskId = taskId, + topComponent = componentName, + screenBounds = taskBounds + ) + } + + suspend fun replaceWithScreenshot( + original: ScreenshotData, + componentName: ComponentName?, + owner: UserHandle?, + displayId: Int, + ): ScreenshotData { + val screenshot = captureDisplay(displayId) + return original.copy( + type = TAKE_SCREENSHOT_FULLSCREEN, + bitmap = screenshot, + userHandle = owner, + topComponent = componentName, + screenBounds = Rect(0, 0, screenshot?.width ?: 0, screenshot?.height ?: 0) + ) + } + + /** TODO: Move to ImageCapture (existing function is non-suspending) */ + private suspend fun captureDisplay(displayId: Int): Bitmap? { + return withContext(background) { capture.captureDisplay(displayId) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt new file mode 100644 index 000000000000..221e64782894 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PrivateProfilePolicy.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 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.screenshot.policy + +import android.os.UserHandle +import com.android.systemui.screenshot.data.model.DisplayContentModel +import com.android.systemui.screenshot.data.model.ProfileType +import com.android.systemui.screenshot.data.repository.ProfileTypeRepository +import com.android.systemui.screenshot.policy.CaptureType.FullScreen +import javax.inject.Inject +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.firstOrNull + +/** + * Condition: When any visible task belongs to a private user. + * + * Parameters: Capture the whole screen, owned by the private user. + */ +class PrivateProfilePolicy +@Inject +constructor( + private val profileTypes: ProfileTypeRepository, +) : CapturePolicy { + override suspend fun apply(content: DisplayContentModel): CaptureParameters? { + // Find the first visible rootTaskInfo with a child task owned by a private user + val (rootTask, childTask) = + content.rootTasks + .filter { it.isVisible } + .firstNotNullOfOrNull { root -> + root + .childTasksTopDown() + .firstOrNull { + profileTypes.getProfileType(it.userId) == ProfileType.PRIVATE + } + ?.let { root to it } + } + ?: return null + + // If matched, return parameters needed to modify the request. + return CaptureParameters( + type = FullScreen(content.displayId), + component = childTask.componentName ?: rootTask.topActivity, + owner = UserHandle.of(childTask.userId), + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt new file mode 100644 index 000000000000..d2f4d9e039f6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/RootTaskInfoExt.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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.screenshot.policy + +import android.app.ActivityTaskManager.RootTaskInfo +import com.android.systemui.screenshot.data.model.ChildTaskModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.map + +internal fun RootTaskInfo.childTasksTopDown(): Flow { + return ((numActivities - 1) downTo 0).asFlow().map { index -> + ChildTaskModel( + childTaskIds[index], + childTaskNames[index], + childTaskBounds[index], + childTaskUserIds[index] + ) + } +} + +internal suspend fun RootTaskInfo.firstChildTaskOrNull( + filter: suspend (Int) -> Boolean +): Pair? { + // Child tasks are provided in bottom-up order + // Filtering is done top-down, so iterate backwards here. + for (index in numActivities - 1 downTo 0) { + if (filter(index)) { + return (this to index) + } + } + return null +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt index bc71ab71b626..63d15087d12c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt @@ -16,7 +16,9 @@ package com.android.systemui.screenshot.policy +import com.android.systemui.Flags.screenshotPrivateProfile import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.screenshot.ImageCapture import com.android.systemui.screenshot.RequestProcessor import com.android.systemui.screenshot.ScreenshotPolicy @@ -29,6 +31,7 @@ import dagger.Binds import dagger.Module import dagger.Provides import javax.inject.Provider +import kotlinx.coroutines.CoroutineDispatcher @Module interface ScreenshotPolicyModule { @@ -37,18 +40,42 @@ interface ScreenshotPolicyModule { @SysUISingleton fun bindProfileTypeRepository(impl: ProfileTypeRepositoryImpl): ProfileTypeRepository + @Binds + @SysUISingleton + fun bindDisplayContentRepository(impl: DisplayContentRepositoryImpl): DisplayContentRepository + companion object { + @JvmStatic + @Provides + @SysUISingleton + fun bindCapturePolicyList( + privateProfilePolicy: PrivateProfilePolicy, + workProfilePolicy: WorkProfilePolicy, + ): List { + // In order of priority. The first matching policy applies. + return listOf(workProfilePolicy, privateProfilePolicy) + } + + @JvmStatic @Provides @SysUISingleton fun bindScreenshotRequestProcessor( + @Background background: CoroutineDispatcher, imageCapture: ImageCapture, policyProvider: Provider, + displayContentRepoProvider: Provider, + policyListProvider: Provider>, ): ScreenshotRequestProcessor { - return RequestProcessor(imageCapture, policyProvider.get()) + return if (screenshotPrivateProfile()) { + PolicyRequestProcessor( + background, + imageCapture, + displayContentRepoProvider.get(), + policyListProvider.get() + ) + } else { + RequestProcessor(imageCapture, policyProvider.get()) + } } } - - @Binds - @SysUISingleton - fun bindDisplayContentRepository(impl: DisplayContentRepositoryImpl): DisplayContentRepository } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt new file mode 100644 index 000000000000..d6b5d6dfda25 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 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.screenshot.policy + +import android.app.WindowConfiguration.WINDOWING_MODE_PINNED +import android.os.UserHandle +import com.android.systemui.screenshot.data.model.DisplayContentModel +import com.android.systemui.screenshot.data.model.ProfileType +import com.android.systemui.screenshot.data.repository.ProfileTypeRepository +import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask +import javax.inject.Inject +import kotlinx.coroutines.flow.first + +/** + * Condition: When the top visible task (excluding PIP mode) belongs to a work user. + * + * Parameters: Capture only the foreground task, owned by the work user. + */ +class WorkProfilePolicy +@Inject +constructor( + private val profileTypes: ProfileTypeRepository, +) : CapturePolicy { + override suspend fun apply(content: DisplayContentModel): CaptureParameters? { + // Find the first non PiP rootTask with a top child task owned by a work user + val (rootTask, childTask) = + content.rootTasks + .filter { it.isVisible && it.windowingMode != WINDOWING_MODE_PINNED } + .map { it to it.childTasksTopDown().first() } + .firstOrNull { (_, child) -> + profileTypes.getProfileType(child.userId) == ProfileType.WORK + } + ?: return null + + // If matched, return parameters needed to modify the request. + return CaptureParameters( + type = IsolatedTask(taskId = childTask.id, taskBounds = childTask.bounds), + component = childTask.componentName ?: rootTask.topActivity, + owner = UserHandle.of(childTask.userId), + ) + } +} -- cgit v1.2.3-59-g8ed1b From eba137145819e87717278e9036e07b0c26b1aa17 Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Fri, 5 Apr 2024 10:18:07 -0400 Subject: Use existing screenshot if present when type is FULLSCREEN When ScreenshotController receives a ScrenshotData with TAKE_SCREENSHOT_FULL_SCREEN, but a non-null Bitmap, treat it the same as if it were TAKE_SCREENSHOT_PROVIDED_IMAGE. This allows ScreenshotRequestProcessor to handle all cases, while not affecting the accuracy of logging. Rationale: ScreenshotRequestProcessor is responsible for populating the top component name, but defers to ScreenshotController to actually capture the screen in full-screen cases. With this change (and new PolicyRequestProcessor) the screen will always be captured before moving to ScreenshotController. This change is safe unflagged because this particular condition does not occur currently. It will be a state provided when the flag 'screenshot_private_profile' is enabled. Bug: 327613051 Test: atest TakeSCreenshotExecutorTest TakeScreenshotServiceTest Flag: N/A Change-Id: If32a01f04d3cf0015817297db4c06a96da8057e8 --- .../src/com/android/systemui/screenshot/ScreenshotController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 047ecb42287b..763b45addbcf 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -392,10 +392,10 @@ public class ScreenshotController { Assert.isMainThread(); mCurrentRequestCallback = requestCallback; - if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_FULLSCREEN) { + if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_FULLSCREEN + && screenshot.getBitmap() == null) { Rect bounds = getFullScreenRect(); - screenshot.setBitmap( - mImageCapture.captureDisplay(mDisplayId, bounds)); + screenshot.setBitmap(mImageCapture.captureDisplay(mDisplayId, bounds)); screenshot.setScreenBounds(bounds); } -- cgit v1.2.3-59-g8ed1b