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