1 /* 2 * Copyright 2023 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 androidx.camera.camera2.pipe.compat 18 19 import android.os.Build 20 import androidx.camera.camera2.pipe.CameraGraph 21 import androidx.camera.camera2.pipe.CameraGraph.RepeatingRequestRequirementsBeforeCapture.CompletionBehavior.AT_LEAST 22 import androidx.camera.camera2.pipe.CameraGraph.RepeatingRequestRequirementsBeforeCapture.CompletionBehavior.EXACT 23 import androidx.camera.camera2.pipe.CameraId 24 import androidx.camera.camera2.pipe.CameraMetadata.Companion.isHardwareLevelLegacy 25 import javax.inject.Inject 26 import javax.inject.Singleton 27 import kotlin.math.max 28 29 @Singleton 30 internal class Camera2Quirks 31 @Inject 32 constructor( 33 private val metadataProvider: Camera2MetadataProvider, 34 ) { 35 /** 36 * A quirk that waits for the last repeating capture request to start before stopping the 37 * current capture session. This is an issue in the Android camera framework where recreating a 38 * capture session too quickly can cause it to deadlock itself (stuck in its idle state), 39 * preventing us from successfully recreating a capture session. 40 * - Bug(s): b/146773463, b/267557892 41 * - Device(s): Camera devices on hardware level LEGACY 42 * - API levels: All 43 */ shouldWaitForRepeatingRequestStartOnDisconnectnull44 internal fun shouldWaitForRepeatingRequestStartOnDisconnect( 45 graphConfig: CameraGraph.Config 46 ): Boolean { 47 // First, check for overrides. 48 graphConfig.flags.awaitRepeatingRequestOnDisconnect?.let { 49 return it 50 } 51 52 // Then we verify whether we need this quirk based on hardware level. 53 return metadataProvider.awaitCameraMetadata(graphConfig.camera).isHardwareLevelLegacy 54 } 55 56 /** 57 * A quirk that creates a blank capture session before closing the camera. This is an issue in 58 * the Android camera framework where it doesn't disconnect the current Surfaces when the camera 59 * device is closed. For this reason, we create a blank capture session, and during which, the 60 * camera framework would disconnect the Surfaces. Another key thing to note is we also need to 61 * wait for the capture session to be configured, since the Surface disconnect calls are done 62 * almost at the very end of session configuration. 63 * - Bug(s): b/128600230, b/267559562 64 * - Device(s): Camera devices on hardware level LEGACY 65 * - API levels: 24 (N) – 28 (P) 66 */ shouldCreateEmptyCaptureSessionBeforeClosingnull67 internal fun shouldCreateEmptyCaptureSessionBeforeClosing(cameraId: CameraId): Boolean { 68 return Build.VERSION.SDK_INT in (Build.VERSION_CODES.N..Build.VERSION_CODES.P) && 69 metadataProvider.awaitCameraMetadata(cameraId).isHardwareLevelLegacy 70 } 71 72 /** 73 * A quirk that waits for [android.hardware.camera2.CameraDevice.StateCallback.onClosed] to come 74 * back before finalizing the current session during camera close. This is needed because on 75 * legacy camera devices, releasing a Surface while camera frames are still being produced would 76 * trigger crashes. 77 * - Bug(s): b/130759707 78 * - Device(s): Camera devices on hardware level LEGACY 79 * - API levels: All 80 */ shouldWaitForCameraDeviceOnClosednull81 internal fun shouldWaitForCameraDeviceOnClosed(cameraId: CameraId): Boolean = 82 metadataProvider.awaitCameraMetadata(cameraId).isHardwareLevelLegacy 83 84 /** 85 * A quirk that closes the camera devices before creating a new capture session. This is needed 86 * on certain devices where creating a capture session directly may lead to deadlocks, NPEs or 87 * other undesirable behaviors. When [shouldCreateEmptyCaptureSessionBeforeClosing] is also 88 * required, a regular camera device closure would then be expanded to: 89 * 1. Close the camera device. 90 * 2. Open the camera device. 91 * 3. Create an empty capture session. 92 * 4. Close the capture session. 93 * 5. Close the camera device. 94 * - Bug(s): b/237341513, b/359062845, b/342263275, b/379347826, b/359062845 95 * - Device(s): Camera devices on hardware level LEGACY 96 * - API levels: 23 (M) – 31 (S_V2) 97 */ 98 internal fun shouldCloseCameraBeforeCreatingCaptureSession(cameraId: CameraId): Boolean { 99 val isLegacyDevice = 100 Build.VERSION.SDK_INT in (Build.VERSION_CODES.M..Build.VERSION_CODES.S_V2) && 101 metadataProvider.awaitCameraMetadata(cameraId).isHardwareLevelLegacy 102 val isQuirkyDevice = 103 "motorola".equals(Build.BRAND, ignoreCase = true) && 104 "moto e20".equals(Build.MODEL, ignoreCase = true) && 105 cameraId.value == "1" 106 return isLegacyDevice || isQuirkyDevice 107 } 108 109 companion object { 110 private val SHOULD_WAIT_FOR_REPEATING_DEVICE_MAP = 111 mapOf( 112 "Google" to setOf("oriole", "raven", "bluejay", "panther", "cheetah", "lynx"), 113 ) 114 115 /** 116 * Returns the number of repeating requests frames before capture for quirks. 117 * 118 * This kind of quirk behavior requires waiting for a certain number of repeating requests 119 * to complete before allowing (single) capture requests to be issued. This is needed on 120 * some devices where issuing a capture request too early might cause it to fail prematurely 121 * or cause some other problem. A value of zero is returned when not required. 122 * - Bug(s): b/287020251, b/289284907 123 * - Device(s): See [SHOULD_WAIT_FOR_REPEATING_DEVICE_MAP] 124 * - API levels: Before 34 (U) 125 */ getRepeatingRequestFrameCountForCapturenull126 internal fun getRepeatingRequestFrameCountForCapture( 127 graphConfigFlags: CameraGraph.Flags 128 ): Int { 129 val requirements = graphConfigFlags.awaitRepeatingRequestBeforeCapture 130 131 var frameCount = 0 132 133 if ( 134 SHOULD_WAIT_FOR_REPEATING_DEVICE_MAP[Build.MANUFACTURER]?.contains(Build.DEVICE) == 135 true && Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE 136 ) { 137 frameCount = max(frameCount, 10) 138 } 139 140 frameCount = 141 when (requirements.completionBehavior) { 142 AT_LEAST -> max(frameCount, requirements.repeatingFramesToComplete.toInt()) 143 EXACT -> requirements.repeatingFramesToComplete.toInt() 144 } 145 146 return frameCount 147 } 148 149 /** 150 * A quirk that calls CameraExtensionCharacteristics before opening an Extension session. 151 * This is an issue in the Android camera framework where Camera2 has a global variable 152 * recording if advanced extensions are supported or not, and the variable is updated the 153 * first time CameraExtensionCharacteristics are queried. If CameraExtensionCharacteristics 154 * are not queried and therefore the variable is not set, Camera2 will fall back to basic 155 * extensions, even if they are not supported, causing the session creation to fail. 156 * - Bug(s): b/293473614 157 * - Device(s): All devices that support advanced extensions 158 * - API levels: Before 34 (U) 159 */ shouldGetExtensionCharacteristicsBeforeSessionnull160 internal fun shouldGetExtensionCharacteristicsBeforeSession(): Boolean { 161 return Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE 162 } 163 } 164 } 165