1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.screenshot 18 19 import android.app.IActivityTaskManager 20 import android.graphics.Bitmap 21 import android.graphics.Rect 22 import android.hardware.display.DisplayManager 23 import android.os.IBinder 24 import android.util.Log 25 import android.view.DisplayAddress 26 import android.view.SurfaceControl 27 import android.view.SurfaceControl.DisplayCaptureArgs 28 import android.view.SurfaceControl.ScreenshotHardwareBuffer 29 import androidx.annotation.VisibleForTesting 30 import com.android.systemui.dagger.SysUISingleton 31 import com.android.systemui.dagger.qualifiers.Background 32 import javax.inject.Inject 33 import kotlinx.coroutines.CoroutineDispatcher 34 import kotlinx.coroutines.withContext 35 36 private const val TAG = "ImageCaptureImpl" 37 38 @SysUISingleton 39 open class ImageCaptureImpl @Inject constructor( 40 private val displayManager: DisplayManager, 41 private val atmService: IActivityTaskManager, 42 @Background private val bgContext: CoroutineDispatcher 43 ) : ImageCapture { 44 captureDisplaynull45 override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? { 46 val width = crop?.width() ?: 0 47 val height = crop?.height() ?: 0 48 val sourceCrop = crop ?: Rect() 49 val displayToken = physicalDisplayToken(displayId) ?: return null 50 val buffer = captureDisplay(displayToken, width, height, sourceCrop) 51 52 return buffer?.asBitmap() 53 } 54 captureTasknull55 override suspend fun captureTask(taskId: Int): Bitmap? { 56 val snapshot = withContext(bgContext) { atmService.takeTaskSnapshot(taskId) } ?: return null 57 return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace) 58 } 59 60 @VisibleForTesting physicalDisplayTokennull61 open fun physicalDisplayToken(displayId: Int): IBinder? { 62 val display = displayManager.getDisplay(displayId) 63 if (display == null) { 64 Log.e(TAG, "No display with id: $displayId") 65 return null 66 } 67 val address = display.address 68 if (address !is DisplayAddress.Physical) { 69 Log.e(TAG, "Display does not have a physical address: $display") 70 return null 71 } 72 return SurfaceControl.getPhysicalDisplayToken(address.physicalDisplayId) 73 } 74 75 @VisibleForTesting captureDisplaynull76 open fun captureDisplay( 77 displayToken: IBinder, 78 width: Int, 79 height: Int, 80 crop: Rect 81 ): ScreenshotHardwareBuffer? { 82 val captureArgs = 83 DisplayCaptureArgs.Builder(displayToken) 84 .setSize(width, height) 85 .setSourceCrop(crop) 86 .build() 87 return SurfaceControl.captureDisplay(captureArgs) 88 } 89 } 90