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.impl 18 19 import androidx.camera.camera2.pipe.core.Log.debug 20 import androidx.camera.camera2.pipe.core.Log.warn 21 import androidx.camera.camera2.pipe.integration.adapter.awaitUntil 22 import androidx.camera.camera2.pipe.integration.adapter.propagateTo 23 import androidx.camera.camera2.pipe.integration.compat.workaround.UseFlashModeTorchFor3aUpdate 24 import androidx.camera.camera2.pipe.integration.config.CameraScope 25 import androidx.camera.camera2.pipe.integration.impl.TorchControl.TorchMode 26 import androidx.camera.core.CameraControl 27 import androidx.camera.core.ImageCapture 28 import androidx.camera.core.ImageCapture.ScreenFlash 29 import androidx.camera.core.ImageCapture.ScreenFlashListener 30 import androidx.camera.core.impl.CameraControlInternal 31 import dagger.Binds 32 import dagger.Module 33 import dagger.multibindings.IntoSet 34 import java.util.concurrent.TimeUnit 35 import javax.inject.Inject 36 import kotlinx.coroutines.CompletableDeferred 37 import kotlinx.coroutines.Deferred 38 import kotlinx.coroutines.Dispatchers 39 import kotlinx.coroutines.async 40 import kotlinx.coroutines.awaitAll 41 import kotlinx.coroutines.withContext 42 43 internal const val DEFAULT_FLASH_MODE = ImageCapture.FLASH_MODE_OFF 44 45 /** Implementation of Flash control exposed by [CameraControlInternal]. */ 46 @CameraScope 47 public class FlashControl 48 @Inject 49 constructor( 50 private val cameraProperties: CameraProperties, 51 private val state3AControl: State3AControl, 52 private val threads: UseCaseThreads, 53 private val torchControl: TorchControl, 54 private val useFlashModeTorchFor3aUpdate: UseFlashModeTorchFor3aUpdate, 55 ) : UseCaseCameraControl { 56 private var _requestControl: UseCaseCameraRequestControl? = null 57 override var requestControl: UseCaseCameraRequestControl? 58 get() = _requestControl 59 set(value) { 60 _requestControl = value 61 setFlashAsync(_flashMode, false) 62 } 63 64 override fun reset() { 65 _flashMode = DEFAULT_FLASH_MODE 66 _screenFlash = null 67 stopRunningTask() 68 setFlashAsync(DEFAULT_FLASH_MODE) 69 } 70 71 @Volatile @ImageCapture.FlashMode private var _flashMode: Int = DEFAULT_FLASH_MODE 72 73 @ImageCapture.FlashMode 74 public var flashMode: Int = _flashMode 75 get() = _flashMode 76 private set 77 78 @Volatile private var _screenFlash: ScreenFlash? = null 79 80 public var screenFlash: ScreenFlash? = _screenFlash 81 get() = _screenFlash 82 private set 83 84 private var _updateSignal: CompletableDeferred<Unit>? = null 85 86 public var updateSignal: Deferred<Unit> = CompletableDeferred(Unit) 87 get() = 88 if (_updateSignal != null) { 89 _updateSignal!! 90 } else { 91 CompletableDeferred(Unit) 92 } 93 private set 94 95 public fun setFlashAsync( 96 @ImageCapture.FlashMode flashMode: Int, 97 cancelPreviousTask: Boolean = true 98 ): Deferred<Unit> { 99 debug { "setFlashAsync: flashMode = $flashMode, requestControl = $requestControl" } 100 val signal = CompletableDeferred<Unit>() 101 102 requestControl?.let { 103 104 // Update _flashMode immediately so that CameraControlInternal#getFlashMode() 105 // returns correct value. 106 _flashMode = flashMode 107 108 if (cancelPreviousTask) { 109 stopRunningTask() 110 } else { 111 // Propagate the result to the previous updateSignal 112 _updateSignal?.let { previousUpdateSignal -> 113 signal.propagateTo(previousUpdateSignal) 114 } 115 } 116 117 _updateSignal = signal 118 state3AControl.flashMode = flashMode 119 state3AControl.updateSignal?.propagateTo(signal) ?: run { signal.complete(Unit) } 120 } 121 ?: run { 122 signal.completeExceptionally( 123 CameraControl.OperationCanceledException("Camera is not active.") 124 ) 125 } 126 127 return signal 128 } 129 130 private fun stopRunningTask() { 131 _updateSignal?.apply { 132 completeExceptionally( 133 CameraControl.OperationCanceledException( 134 "There is a new flash mode being set or camera was closed" 135 ) 136 ) 137 } 138 _updateSignal = null 139 } 140 141 public fun setScreenFlash(screenFlash: ScreenFlash?) { 142 _screenFlash = screenFlash 143 } 144 145 public suspend fun startScreenFlashCaptureTasks() { 146 val pendingTasks = mutableListOf<Deferred<Unit>>() 147 148 // Invoke ScreenFlash#apply and wait later for its listener to be completed 149 pendingTasks.add( 150 applyScreenFlash( 151 TimeUnit.SECONDS.toMillis(ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS) 152 ) 153 ) 154 155 // Try to set external flash AE mode if possible 156 setExternalFlashAeModeAsync()?.let { pendingTasks.add(it) } 157 158 // Set FLASH_MODE_TORCH for quirks 159 setTorchForScreenFlash()?.let { pendingTasks.add(it) } 160 161 pendingTasks.awaitAll() 162 } 163 164 /** 165 * Invokes [ScreenFlash.apply] immediately and returns a [Deferred] waiting for the 166 * [ScreenFlashListener] to be completed. 167 */ 168 private suspend fun applyScreenFlash(timeoutMillis: Long): Deferred<Unit> { 169 val onApplyCompletedSignal = CompletableDeferred<Unit>() 170 val screenFlashListener = ScreenFlashListener { onApplyCompletedSignal.complete(Unit) } 171 172 withContext(Dispatchers.Main) { 173 val expirationTimeMillis = System.currentTimeMillis() + timeoutMillis 174 screenFlash?.apply(expirationTimeMillis, screenFlashListener) 175 debug { 176 "applyScreenFlash: ScreenFlash.apply() invoked" + 177 ", expirationTimeMillis = $expirationTimeMillis" 178 } 179 } 180 181 return threads.scope.async { 182 debug { "applyScreenFlash: Waiting for ScreenFlashListener to be completed" } 183 184 // Wait for ScreenFlashListener#onCompleted to be invoked, 185 // it's ok to give a little more time than expirationTimeMillis in ScreenFlash#apply 186 187 if (onApplyCompletedSignal.awaitUntil(timeoutMillis)) { 188 debug { "applyScreenFlash: ScreenFlashListener completed" } 189 } else { 190 warn { 191 "applyScreenFlash: ScreenFlashListener completion timed out" + 192 " after $timeoutMillis ms" 193 } 194 } 195 } 196 } 197 198 /** 199 * Tries to set external flash AE mode if possible. 200 * 201 * @return A [Deferred] that reports the completion of the operation, `null` if not supported. 202 */ 203 private fun setExternalFlashAeModeAsync(): Deferred<Unit>? { 204 val isExternalFlashAeModeSupported = 205 cameraProperties.metadata.isExternalFlashAeModeSupported() 206 debug { 207 "setExternalFlashAeModeAsync: isExternalFlashAeModeSupported = " + 208 "$isExternalFlashAeModeSupported" 209 } 210 211 if (!isExternalFlashAeModeSupported) { 212 return null 213 } 214 215 state3AControl.tryExternalFlashAeMode = true 216 return state3AControl.updateSignal?.also { 217 debug { "setExternalFlashAeModeAsync: need to wait for state3AControl.updateSignal" } 218 it.invokeOnCompletion { 219 debug { "setExternalFlashAeModeAsync: state3AControl.updateSignal completed" } 220 } 221 } 222 } 223 224 /** 225 * Enables the torch mode for screen flash capture when required. 226 * 227 * Since this is required due to a device quirk despite lacking physical flash unit, the 228 * `ignoreFlashUnitAvailability` parameter is set to `true` while invoking 229 * [TorchControl.setTorchAsync]. 230 * 231 * @return A [Deferred] that reports the completion of the operation, `null` if not required. 232 */ 233 private fun setTorchForScreenFlash(): Deferred<Unit>? { 234 val shouldUseFlashModeTorch = useFlashModeTorchFor3aUpdate.shouldUseFlashModeTorch() 235 debug { "setTorchIfRequired: shouldUseFlashModeTorch = $shouldUseFlashModeTorch" } 236 237 if (!shouldUseFlashModeTorch) { 238 return null 239 } 240 241 return torchControl 242 .setTorchAsync(mode = TorchMode.USED_AS_FLASH, ignoreFlashUnitAvailability = true) 243 .also { 244 debug { "setTorchIfRequired: need to wait for torch control to be completed" } 245 it.invokeOnCompletion { debug { "setTorchIfRequired: torch control completed" } } 246 } 247 } 248 249 public suspend fun stopScreenFlashCaptureTasks() { 250 withContext(Dispatchers.Main) { 251 screenFlash?.clear() 252 debug { "screenFlashPostCapture: ScreenFlash.clear() invoked" } 253 } 254 255 if (cameraProperties.metadata.isExternalFlashAeModeSupported()) { 256 // Disable external flash AE mode, ok to complete whenever 257 state3AControl.tryExternalFlashAeMode = false 258 } 259 260 if (useFlashModeTorchFor3aUpdate.shouldUseFlashModeTorch()) { 261 torchControl.setTorchAsync(mode = TorchMode.OFF, ignoreFlashUnitAvailability = true) 262 } 263 } 264 265 /** 266 * Awaits for flash mode to be updated (if required) and returns the initial flash mode value 267 * i.e. the value for which the waiting was started. 268 */ 269 public suspend fun awaitFlashModeUpdate(): Int { 270 debug { "FlashControl: Waiting for any ongoing update to be completed" } 271 // The flash mode may change while waiting for it to be updated, snapshotting it to ensure 272 // the initial flash mode value (for which waiting started) is returned afterwards. 273 val initialFlashMode = flashMode 274 updateSignal.join() 275 debug { "awaitFlashModeUpdate: initialFlashMode = $initialFlashMode" } 276 return initialFlashMode 277 } 278 279 @Module 280 public abstract class Bindings { 281 @Binds 282 @IntoSet 283 public abstract fun provideControls(flashControl: FlashControl): UseCaseCameraControl 284 } 285 } 286