1 /* 2 * Copyright 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 androidx.camera.camera2.pipe.integration.adapter 18 19 import android.os.Looper 20 import androidx.annotation.GuardedBy 21 import androidx.camera.camera2.pipe.CameraError 22 import androidx.camera.camera2.pipe.CameraGraph 23 import androidx.camera.camera2.pipe.GraphState 24 import androidx.camera.camera2.pipe.GraphState.GraphStateError 25 import androidx.camera.camera2.pipe.GraphState.GraphStateStarted 26 import androidx.camera.camera2.pipe.GraphState.GraphStateStarting 27 import androidx.camera.camera2.pipe.GraphState.GraphStateStopped 28 import androidx.camera.camera2.pipe.GraphState.GraphStateStopping 29 import androidx.camera.camera2.pipe.core.Log 30 import androidx.camera.camera2.pipe.integration.config.CameraScope 31 import androidx.camera.core.CameraState 32 import androidx.camera.core.impl.CameraInternal 33 import androidx.camera.core.impl.LiveDataObservable 34 import androidx.lifecycle.MutableLiveData 35 import javax.inject.Inject 36 37 @CameraScope 38 public class CameraStateAdapter @Inject constructor() { 39 private val lock = Any() 40 41 internal val cameraInternalState = LiveDataObservable<CameraInternal.State>() 42 internal val cameraState = MutableLiveData<CameraState>() 43 44 @GuardedBy("lock") private var currentGraph: CameraGraph? = null 45 46 @GuardedBy("lock") private var currentCameraInternalState = CameraInternal.State.CLOSED 47 48 @GuardedBy("lock") private var currentCameraStateError: CameraState.StateError? = null 49 50 init { 51 postCameraState(CameraInternal.State.CLOSED) 52 } 53 onGraphUpdatednull54 public fun onGraphUpdated(cameraGraph: CameraGraph): Unit = 55 synchronized(lock) { 56 Log.debug { "Camera graph updated from $currentGraph to $cameraGraph" } 57 if (currentCameraInternalState != CameraInternal.State.CLOSED) { 58 postCameraState(CameraInternal.State.CLOSING) 59 postCameraState(CameraInternal.State.CLOSED) 60 } 61 currentGraph = cameraGraph 62 currentCameraInternalState = CameraInternal.State.CLOSED 63 } 64 onGraphStateUpdatednull65 public fun onGraphStateUpdated(cameraGraph: CameraGraph, graphState: GraphState): Unit = 66 synchronized(lock) { 67 Log.debug { "$cameraGraph state updated to $graphState" } 68 handleStateTransition(cameraGraph, graphState) 69 } 70 71 @GuardedBy("lock") handleStateTransitionnull72 private fun handleStateTransition(cameraGraph: CameraGraph, graphState: GraphState) { 73 // If the transition came from a different camera graph, consider it stale and ignore it. 74 if (cameraGraph != currentGraph) { 75 Log.debug { "Ignored stale transition $graphState for $cameraGraph" } 76 return 77 } 78 79 val nextComboState = calculateNextState(currentCameraInternalState, graphState) 80 if (nextComboState == null) { 81 Log.warn { 82 "Impermissible state transition: " + 83 "current camera internal state: $currentCameraInternalState, " + 84 "received graph state: $graphState" 85 } 86 return 87 } 88 currentCameraInternalState = nextComboState.state 89 currentCameraStateError = nextComboState.error 90 91 // Now that the current graph state is updated, post the latest states. 92 Log.debug { "Updated current camera internal state to $currentCameraInternalState" } 93 postCameraState(currentCameraInternalState, currentCameraStateError) 94 } 95 postCameraStatenull96 private fun postCameraState( 97 internalState: CameraInternal.State, 98 stateError: CameraState.StateError? = null 99 ) { 100 cameraInternalState.postValue(internalState) 101 cameraState.setOrPostValue(CameraState.create(internalState.toCameraState(), stateError)) 102 } 103 104 /** 105 * Calculates the next CameraX camera internal state based on the current camera internal state 106 * and the graph state received from CameraGraph. Returns null when there's no permissible state 107 * transition. 108 */ calculateNextStatenull109 internal fun calculateNextState( 110 currentState: CameraInternal.State, 111 graphState: GraphState 112 ): CombinedCameraState? = 113 when (currentState) { 114 CameraInternal.State.CLOSED -> 115 when (graphState) { 116 GraphStateStarting -> CombinedCameraState(CameraInternal.State.OPENING) 117 GraphStateStarted -> CombinedCameraState(CameraInternal.State.OPEN) 118 else -> null 119 } 120 CameraInternal.State.OPENING -> 121 when (graphState) { 122 GraphStateStarted -> CombinedCameraState(CameraInternal.State.OPEN) 123 is GraphStateError -> 124 if (graphState.willAttemptRetry) { 125 CombinedCameraState( 126 CameraInternal.State.OPENING, 127 graphState.cameraError.toCameraStateError() 128 ) 129 } else { 130 if (isRecoverableError(graphState.cameraError)) { 131 CombinedCameraState( 132 CameraInternal.State.PENDING_OPEN, 133 graphState.cameraError.toCameraStateError() 134 ) 135 } else { 136 CombinedCameraState( 137 CameraInternal.State.CLOSING, 138 graphState.cameraError.toCameraStateError() 139 ) 140 } 141 } 142 GraphStateStopping -> CombinedCameraState(CameraInternal.State.CLOSING) 143 GraphStateStopped -> CombinedCameraState(CameraInternal.State.CLOSED) 144 else -> null 145 } 146 CameraInternal.State.OPEN -> 147 when (graphState) { 148 GraphStateStopping -> CombinedCameraState(CameraInternal.State.CLOSING) 149 GraphStateStopped -> CombinedCameraState(CameraInternal.State.CLOSED) 150 is GraphStateError -> 151 if (isRecoverableError(graphState.cameraError)) { 152 CombinedCameraState( 153 CameraInternal.State.PENDING_OPEN, 154 graphState.cameraError.toCameraStateError() 155 ) 156 } else { 157 CombinedCameraState( 158 CameraInternal.State.CLOSED, 159 graphState.cameraError.toCameraStateError() 160 ) 161 } 162 else -> null 163 } 164 CameraInternal.State.CLOSING -> 165 when (graphState) { 166 GraphStateStopped -> CombinedCameraState(CameraInternal.State.CLOSED) 167 GraphStateStarting -> CombinedCameraState(CameraInternal.State.OPENING) 168 is GraphStateError -> 169 CombinedCameraState( 170 CameraInternal.State.CLOSING, 171 graphState.cameraError.toCameraStateError() 172 ) 173 else -> null 174 } 175 CameraInternal.State.PENDING_OPEN -> 176 when (graphState) { 177 GraphStateStarting -> CombinedCameraState(CameraInternal.State.OPENING) 178 GraphStateStarted -> CombinedCameraState(CameraInternal.State.OPEN) 179 is GraphStateError -> 180 if (isRecoverableError(graphState.cameraError)) { 181 CombinedCameraState( 182 CameraInternal.State.PENDING_OPEN, 183 graphState.cameraError.toCameraStateError() 184 ) 185 } else { 186 CombinedCameraState( 187 CameraInternal.State.CLOSED, 188 graphState.cameraError.toCameraStateError() 189 ) 190 } 191 else -> null 192 } 193 else -> null 194 } 195 196 internal data class CombinedCameraState( 197 val state: CameraInternal.State, 198 val error: CameraState.StateError? = null 199 ) 200 201 public companion object { toCameraStateErrornull202 internal fun CameraError.toCameraStateError(): CameraState.StateError = 203 CameraState.StateError.create( 204 when (this) { 205 CameraError.ERROR_UNDETERMINED -> CameraState.ERROR_CAMERA_FATAL_ERROR 206 CameraError.ERROR_CAMERA_IN_USE -> CameraState.ERROR_CAMERA_IN_USE 207 CameraError.ERROR_CAMERA_LIMIT_EXCEEDED -> CameraState.ERROR_MAX_CAMERAS_IN_USE 208 CameraError.ERROR_CAMERA_DISABLED -> CameraState.ERROR_CAMERA_DISABLED 209 CameraError.ERROR_CAMERA_DEVICE -> CameraState.ERROR_OTHER_RECOVERABLE_ERROR 210 CameraError.ERROR_CAMERA_SERVICE -> CameraState.ERROR_CAMERA_FATAL_ERROR 211 CameraError.ERROR_CAMERA_DISCONNECTED -> 212 CameraState.ERROR_OTHER_RECOVERABLE_ERROR 213 CameraError.ERROR_ILLEGAL_ARGUMENT_EXCEPTION -> 214 CameraState.ERROR_CAMERA_FATAL_ERROR 215 CameraError.ERROR_SECURITY_EXCEPTION -> CameraState.ERROR_CAMERA_FATAL_ERROR 216 CameraError.ERROR_GRAPH_CONFIG -> CameraState.ERROR_STREAM_CONFIG 217 CameraError.ERROR_DO_NOT_DISTURB_ENABLED -> 218 CameraState.ERROR_DO_NOT_DISTURB_MODE_ENABLED 219 CameraError.ERROR_UNKNOWN_EXCEPTION -> CameraState.ERROR_CAMERA_FATAL_ERROR 220 else -> throw IllegalArgumentException("Unexpected CameraError: $this") 221 } 222 ) 223 toCameraStatenull224 internal fun CameraInternal.State.toCameraState(): CameraState.Type = 225 when (this) { 226 CameraInternal.State.CLOSED -> CameraState.Type.CLOSED 227 CameraInternal.State.OPENING -> CameraState.Type.OPENING 228 CameraInternal.State.OPEN -> CameraState.Type.OPEN 229 CameraInternal.State.CLOSING -> CameraState.Type.CLOSING 230 CameraInternal.State.PENDING_OPEN -> CameraState.Type.PENDING_OPEN 231 else -> throw IllegalArgumentException("Unexpected CameraInternal state: $this") 232 } 233 isRecoverableErrornull234 internal fun isRecoverableError(cameraError: CameraError) = 235 cameraError == CameraError.ERROR_CAMERA_DISCONNECTED || 236 cameraError == CameraError.ERROR_CAMERA_IN_USE || 237 cameraError == CameraError.ERROR_CAMERA_LIMIT_EXCEEDED || 238 cameraError == CameraError.ERROR_CAMERA_DEVICE 239 240 internal fun MutableLiveData<CameraState>.setOrPostValue(cameraState: CameraState) { 241 if (Looper.myLooper() == Looper.getMainLooper()) { 242 this.value = cameraState 243 } else { 244 this.postValue(cameraState) 245 } 246 } 247 } 248 } 249