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