1 /*
<lambda>null2  * Copyright 2021 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.testing
18 
19 import android.hardware.camera2.params.MeteringRectangle
20 import androidx.camera.camera2.pipe.AeMode
21 import androidx.camera.camera2.pipe.AfMode
22 import androidx.camera.camera2.pipe.AwbMode
23 import androidx.camera.camera2.pipe.CameraGraph
24 import androidx.camera.camera2.pipe.Frame
25 import androidx.camera.camera2.pipe.FrameCapture
26 import androidx.camera.camera2.pipe.FrameMetadata
27 import androidx.camera.camera2.pipe.FrameNumber
28 import androidx.camera.camera2.pipe.Lock3ABehavior
29 import androidx.camera.camera2.pipe.OutputStatus
30 import androidx.camera.camera2.pipe.Request
31 import androidx.camera.camera2.pipe.Result3A
32 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraphSession.RequestStatus.ABORTED
33 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraphSession.RequestStatus.FAILED
34 import androidx.camera.camera2.pipe.integration.testing.FakeCameraGraphSession.RequestStatus.TOTAL_CAPTURE_DONE
35 import androidx.camera.camera2.pipe.testing.FakeFrameInfo
36 import androidx.camera.camera2.pipe.testing.FakeRequestFailure
37 import androidx.camera.camera2.pipe.testing.FakeRequestMetadata
38 import java.util.concurrent.Semaphore
39 import kotlinx.atomicfu.atomic
40 import kotlinx.coroutines.CompletableDeferred
41 import kotlinx.coroutines.Deferred
42 import kotlinx.coroutines.ExperimentalCoroutinesApi
43 import kotlinx.coroutines.runBlocking
44 
45 open class FakeCameraGraphSession : CameraGraph.Session {
46 
47     val repeatingRequests = mutableListOf<Request>()
48     var repeatingRequestSemaphore = Semaphore(0)
49     var stopRepeatingSemaphore = Semaphore(0)
50 
51     enum class RequestStatus {
52         TOTAL_CAPTURE_DONE,
53         FAILED,
54         ABORTED
55     }
56 
57     var startRepeatingSignal = CompletableDeferred(TOTAL_CAPTURE_DONE) // already completed
58 
59     val submittedRequests = mutableListOf<Request>()
60 
61     override fun abort() {
62         // No-op
63     }
64 
65     override fun close() {
66         // No-op
67     }
68 
69     override suspend fun lock3A(
70         aeMode: AeMode?,
71         afMode: AfMode?,
72         awbMode: AwbMode?,
73         aeRegions: List<MeteringRectangle>?,
74         afRegions: List<MeteringRectangle>?,
75         awbRegions: List<MeteringRectangle>?,
76         aeLockBehavior: Lock3ABehavior?,
77         afLockBehavior: Lock3ABehavior?,
78         awbLockBehavior: Lock3ABehavior?,
79         afTriggerStartAeMode: AeMode?,
80         convergedCondition: ((FrameMetadata) -> Boolean)?,
81         lockedCondition: ((FrameMetadata) -> Boolean)?,
82         frameLimit: Int,
83         convergedTimeLimitNs: Long,
84         lockedTimeLimitNs: Long
85     ): Deferred<Result3A> {
86         throw NotImplementedError("Not used in testing")
87     }
88 
89     override suspend fun lock3AForCapture(
90         lockedCondition: ((FrameMetadata) -> Boolean)?,
91         frameLimit: Int,
92         timeLimitNs: Long
93     ): Deferred<Result3A> {
94         throw NotImplementedError("Not used in testing")
95     }
96 
97     override suspend fun lock3AForCapture(
98         triggerAf: Boolean,
99         waitForAwb: Boolean,
100         frameLimit: Int,
101         timeLimitNs: Long
102     ): Deferred<Result3A> {
103         throw NotImplementedError("Not used in testing")
104     }
105 
106     override fun setTorchOn(): Deferred<Result3A> {
107         throw NotImplementedError("Not used in testing")
108     }
109 
110     override fun setTorchOff(aeMode: AeMode?): Deferred<Result3A> {
111         throw NotImplementedError("Not used in testing")
112     }
113 
114     override fun startRepeating(request: Request) {
115         repeatingRequests.add(request)
116         repeatingRequestSemaphore.release()
117 
118         startRepeatingSignal.invokeOnCompletion {
119             // completes immediately if startRepeatingListenerInvoker is the initial one
120             runBlocking {
121                 // the real GraphSession processes only the last successful repeating request
122                 repeatingRequests.notifyLastRequestListeners(request, startRepeatingSignal.await())
123             }
124         }
125     }
126 
127     override fun stopRepeating() {
128         stopRepeatingSemaphore.release()
129     }
130 
131     override fun submit(request: Request) {
132         submittedRequests.add(request)
133     }
134 
135     override fun submit(requests: List<Request>) {
136         submittedRequests.addAll(requests)
137     }
138 
139     override fun capture(request: Request): FrameCapture {
140         val capture = FakeFrameCapture(request)
141         submit(request)
142         return capture
143     }
144 
145     override fun capture(requests: List<Request>): List<FrameCapture> {
146         val captures = requests.map { FakeFrameCapture(it) }
147         submit(requests)
148         return captures
149     }
150 
151     override suspend fun submit3A(
152         aeMode: AeMode?,
153         afMode: AfMode?,
154         awbMode: AwbMode?,
155         aeRegions: List<MeteringRectangle>?,
156         afRegions: List<MeteringRectangle>?,
157         awbRegions: List<MeteringRectangle>?
158     ): Deferred<Result3A> {
159         throw NotImplementedError("Not used in testing")
160     }
161 
162     override suspend fun unlock3A(
163         ae: Boolean?,
164         af: Boolean?,
165         awb: Boolean?,
166         unlockedCondition: ((FrameMetadata) -> Boolean)?,
167         frameLimit: Int,
168         timeLimitNs: Long
169     ): Deferred<Result3A> {
170         throw NotImplementedError("Not used in testing")
171     }
172 
173     override suspend fun unlock3APostCapture(cancelAf: Boolean): Deferred<Result3A> {
174         throw NotImplementedError("Not used in testing")
175     }
176 
177     override fun update3A(
178         aeMode: AeMode?,
179         afMode: AfMode?,
180         awbMode: AwbMode?,
181         aeRegions: List<MeteringRectangle>?,
182         afRegions: List<MeteringRectangle>?,
183         awbRegions: List<MeteringRectangle>?
184     ): Deferred<Result3A> {
185         return CompletableDeferred(Result3A(Result3A.Status.OK))
186     }
187 
188     private fun MutableList<Request>.notifyLastRequestListeners(
189         request: Request,
190         status: RequestStatus
191     ) {
192         val requestMetadata = FakeRequestMetadata(request = request)
193         last().listeners.forEach { listener ->
194             when (status) {
195                 TOTAL_CAPTURE_DONE ->
196                     listener.onTotalCaptureResult(requestMetadata, FrameNumber(0), FakeFrameInfo())
197                 FAILED ->
198                     listener.onFailed(
199                         requestMetadata,
200                         FrameNumber(0),
201                         FakeRequestFailure(requestMetadata, FrameNumber(0))
202                     )
203                 ABORTED -> listener.onRequestSequenceAborted(requestMetadata)
204             }
205         }
206     }
207 
208     @OptIn(ExperimentalCoroutinesApi::class)
209     private class FakeFrameCapture(override val request: Request) : FrameCapture {
210         private val result = CompletableDeferred<Frame?>()
211         private val closed = atomic(false)
212         private val listeners = mutableListOf<Frame.Listener>()
213         override val status: OutputStatus
214             get() {
215                 if (closed.value || result.isCancelled) return OutputStatus.UNAVAILABLE
216                 if (!result.isCompleted) return OutputStatus.PENDING
217                 return OutputStatus.AVAILABLE
218             }
219 
220         override suspend fun awaitFrame(): Frame? = result.await()
221 
222         override fun getFrame(): Frame? {
223             if (result.isCompleted && !result.isCancelled) {
224                 return result.getCompleted()
225             }
226             return null
227         }
228 
229         override fun addListener(listener: Frame.Listener) {
230             listeners.add(listener)
231         }
232 
233         override fun close() {
234             if (closed.compareAndSet(expect = false, update = true)) {
235                 result.cancel()
236             }
237         }
238     }
239 }
240