1 /* <lambda>null2 * Copyright (C) 2024 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 android.security.cts.camera.open 18 19 import android.content.AttributionSource 20 import android.content.Context 21 import android.content.ContextParams 22 import android.content.Intent 23 import android.graphics.ImageFormat 24 import android.graphics.SurfaceTexture 25 import android.hardware.Camera 26 import android.hardware.camera2.CameraCaptureSession 27 import android.hardware.camera2.CameraCharacteristics 28 import android.hardware.camera2.CameraDevice 29 import android.hardware.camera2.CameraManager 30 import android.hardware.camera2.CaptureFailure 31 import android.hardware.camera2.CaptureRequest 32 import android.hardware.camera2.TotalCaptureResult 33 import android.hardware.camera2.params.OutputConfiguration 34 import android.hardware.camera2.params.SessionConfiguration 35 import android.media.ImageReader 36 import android.os.Handler 37 import android.os.HandlerThread 38 import android.security.cts.camera.open.lib.ICameraOpener 39 import android.security.cts.camera.open.lib.IntentKeys 40 import android.util.Log 41 import android.view.Surface 42 import android.view.TextureView 43 import java.util.concurrent.Executors 44 import kotlin.coroutines.resume 45 import kotlinx.coroutines.CancellableContinuation 46 import kotlinx.coroutines.runBlocking 47 import kotlinx.coroutines.suspendCancellableCoroutine 48 49 class CameraOpener( 50 private val context: Context, 51 private val keys: IntentKeys, 52 private val textureView: TextureView?, 53 private var surfaceTexture: SurfaceTexture? 54 ) { 55 private lateinit var cameraManager: CameraManager 56 private var handlerThread: HandlerThread? = null 57 private val cameraExecutor = Executors.newSingleThreadExecutor() 58 59 var onStopRepeating: () -> Unit = {} 60 61 val aidlInterface = 62 object : ICameraOpener.Stub() { 63 override fun openCamera1( 64 attributionSource: AttributionSource, 65 shouldStream: Boolean, 66 shouldRepeat: Boolean 67 ): Intent = runBlocking { 68 Log.v(TAG, "AIDL openCamera1") 69 openCamera1Async(shouldStream, shouldRepeat, attributionSource) 70 } 71 72 override fun openCamera2( 73 attributionSource: AttributionSource, 74 shouldStream: Boolean, 75 shouldRepeat: Boolean 76 ): Intent = runBlocking { 77 Log.v(TAG, "AIDL openCamera2") 78 openCamera2Async(shouldStream, shouldRepeat, attributionSource) 79 } 80 81 override fun openCameraNdk( 82 attributionSource: AttributionSource, 83 shouldStream: Boolean, 84 shouldRepeat: Boolean 85 ): Intent = runBlocking { 86 Log.v(TAG, "AIDL openCameraNdk") 87 openCameraNdkAsync(shouldStream, shouldRepeat, attributionSource) 88 } 89 90 override fun stopRepeating() = onStopRepeating() 91 } 92 93 init { 94 System.loadLibrary("opencameraapp_jni") 95 } 96 97 suspend fun openCamera1Async( 98 shouldStream: Boolean, 99 shouldRepeat: Boolean, 100 attributionSource: AttributionSource? = null, 101 ): Intent { 102 val result = Intent().apply { putExtra(keys.attributionSource, attributionSource.toString()) } 103 try { 104 if (Camera.getNumberOfCameras() > 0) { 105 val camera = Camera.open(0) 106 result.putExtra(keys.cameraOpened1, true) 107 if (shouldStream) { 108 return openCamera1Stream(camera, shouldRepeat, result) 109 } else { 110 camera.release() 111 return result 112 } 113 } else { 114 return result.apply { putExtra(keys.noCamera, true) } 115 } 116 } catch (e: Exception) { 117 Log.e(TAG, "Received exception: ${e.message}") 118 return result.apply { putException(keys, e) } 119 } 120 } 121 122 suspend fun openCamera2Async( 123 shouldStream: Boolean, 124 shouldRepeat: Boolean, 125 attributionSource: AttributionSource? = null, 126 ): Intent = suspendCancellableCoroutine { continuation -> 127 val result = Intent().apply { putExtra(keys.attributionSource, attributionSource.toString()) } 128 continuation.tryOrResume(keys, result, "openCamera2Async") { 129 var customContext = context 130 attributionSource?.let { 131 val contextParams = ContextParams.Builder().setNextAttributionSource(it).build() 132 customContext = context.createContext(contextParams) 133 } 134 cameraManager = customContext.getSystemService(Context.CAMERA_SERVICE) as CameraManager 135 if (cameraManager.getCameraIdList().isEmpty()) { 136 continuation.resume(result.apply { putExtra(keys.noCamera, true) }) 137 } 138 139 val cameraId = cameraManager.getCameraIdList()[0] 140 cameraManager.openCamera( 141 cameraId, 142 cameraExecutor, 143 object : CameraDevice.StateCallback() { 144 override fun onOpened(cameraDevice: CameraDevice) { 145 Log.v(TAG, "onOpened") 146 147 result.putExtra(keys.cameraOpened2, true) 148 if (shouldStream) { 149 continuation.tryOrResume(keys, result, "openCamera2Async/onOpened") { 150 openCamera2Stream(cameraDevice, result, continuation, shouldRepeat) 151 } 152 } else { 153 cameraDevice.close() 154 continuation.resume(result) 155 } 156 } 157 158 override fun onDisconnected(cameraDevice: CameraDevice) { 159 Log.v(TAG, "onDisconnected") 160 continuation.tryOrResume(keys, result, "openCamera2Async/onDisconnected") { 161 cameraDevice.close() 162 } 163 } 164 165 override fun onError(cameraDevice: CameraDevice, error: Int) { 166 Log.v(TAG, "onError: " + error) 167 168 try { 169 cameraDevice.close() 170 } catch (e: Exception) { 171 Log.e( 172 TAG, 173 "openCamera2Async/onDisconnected: Received exception: ${e.exceptionString}") 174 result.putException(keys, e) 175 } 176 177 if (continuation.isActive) { // continuation may already have been resumed 178 continuation.resume(result.apply { putExtra(keys.error, error) }) 179 } 180 } 181 }) 182 } 183 } 184 185 suspend fun openCameraNdkAsync( 186 shouldStream: Boolean, 187 shouldRepeat: Boolean, 188 attributionSource: AttributionSource? = null 189 ): Intent = suspendCancellableCoroutine { continuation -> 190 Log.v(TAG, "openCameraNdkAsync: shouldStream ${shouldStream} shouldRepeat ${shouldRepeat}") 191 var result = Intent().apply { putExtra(keys.attributionSource, attributionSource.toString()) } 192 193 // Avoid blocking the main thread 194 cameraExecutor.execute { 195 nativeInit() 196 197 if (!nativeHasCamera()) { 198 continuation.resume(result.apply { putExtra(keys.noCamera, true) }) 199 return@execute 200 } 201 202 val openCameraResult = nativeOpenCamera() 203 if (openCameraResult != 0) { 204 Log.e(TAG, "Failed to open camera: ${openCameraResult}") 205 continuation.resume(result.apply { putExtra(keys.error, openCameraResult) }) 206 return@execute 207 } 208 209 result.putExtra(keys.cameraOpenedNdk, true) 210 if (shouldStream) { 211 openCameraNdkStream(result, continuation, shouldRepeat) 212 } else { 213 continuation.resume(result) 214 } 215 } 216 217 // Run cleanup sequentially after the above 218 cameraExecutor.execute { 219 nativeCleanup() 220 if (continuation.isActive) { 221 continuation.resume(result) 222 } 223 } 224 } 225 226 fun release() { 227 handlerThread?.quitSafely() 228 } 229 230 private suspend fun openCamera1Stream( 231 camera: Camera, 232 shouldRepeat: Boolean, 233 result: Intent 234 ): Intent = suspendCancellableCoroutine { continuation -> 235 val params = camera.getParameters() 236 val previewSize = params.getSupportedPreviewSizes()[0] 237 params.setPreviewSize(previewSize.width, previewSize.height) 238 camera.setParameters(params) 239 240 if (textureView?.surfaceTexture != null) { 241 surfaceTexture = textureView.surfaceTexture 242 } 243 244 if (surfaceTexture != null) { 245 captureCamera1(camera, result, continuation, shouldRepeat) 246 } else if (textureView != null) { 247 textureView.setSurfaceTextureListener( 248 object : TextureView.SurfaceTextureListener { 249 override fun onSurfaceTextureAvailable( 250 surfaceTexture: SurfaceTexture, 251 width: Int, 252 height: Int 253 ) { 254 surfaceTexture.let { 255 this@CameraOpener.surfaceTexture = it 256 captureCamera1(camera, result, continuation, shouldRepeat) 257 } 258 } 259 260 override fun onSurfaceTextureSizeChanged( 261 surface: SurfaceTexture, 262 width: Int, 263 height: Int 264 ) {} 265 266 override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean { 267 Log.v(TAG, "openCamera1Stream/onSurfaceTextureDestroyed") 268 return true 269 } 270 271 override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {} 272 }) 273 } 274 } 275 276 private fun captureCamera1( 277 camera: Camera, 278 result: Intent, 279 continuation: CancellableContinuation<Intent>, 280 shouldRepeat: Boolean, 281 ) { 282 camera.setPreviewTexture(surfaceTexture) 283 284 val cleanup: () -> Unit = { 285 Log.v(TAG, "captureCamera1/cleanup") 286 continuation.tryOrResume(keys, result, "captureCamera1/cleanup") { 287 camera.stopPreview() 288 camera.setPreviewTexture(null) 289 camera.release() 290 } 291 } 292 293 camera.setErrorCallback( 294 object : Camera.ErrorCallback { 295 override fun onError(error: Int, camera: Camera) { 296 Log.v(TAG, "captureCamera1/onError: " + error) 297 298 try { 299 camera.release() 300 } catch (e: Exception) { 301 Log.e(TAG, "captureCamera1/onError: Received exception: ${e.exceptionString}") 302 result.putException(keys, e) 303 } 304 305 if (continuation.isActive) { // continuation may already have been resumed 306 continuation.resume(result.apply { putExtra(keys.error, error) }) 307 } 308 } 309 }) 310 311 camera.setPreviewCallback( 312 object : Camera.PreviewCallback { 313 private var firstCaptureCompleted = false 314 315 override fun onPreviewFrame(data: ByteArray, camera: Camera) { 316 if (!firstCaptureCompleted) { 317 Log.v(TAG, "Camera.PreviewCallback.onPreviewFrame() (first)") 318 onStopRepeating = { 319 Log.v(TAG, "onStopRepeating") 320 cleanup() 321 322 if (continuation.isActive) { 323 continuation.resume(result.apply { putExtra(keys.stoppedRepeating, true) }) 324 } 325 } 326 327 signalStreamOpened(true, result) 328 firstCaptureCompleted = true 329 330 if (!shouldRepeat) { 331 cleanup() 332 continuation.resume(result) 333 } 334 } else { 335 Log.v(TAG, "Camera.PreviewCallback.onPreviewFrame()") 336 } 337 } 338 }) 339 340 camera.startPreview() 341 } 342 343 private fun openCamera2Stream( 344 cameraDevice: CameraDevice, 345 result: Intent, 346 continuation: CancellableContinuation<Intent>, 347 shouldRepeat: Boolean 348 ) { 349 handlerThread = HandlerThread("${context.packageName}.CAPTURE").apply { start() } 350 val captureHandler = Handler(handlerThread!!.getLooper()) 351 352 val characteristics = cameraManager.getCameraCharacteristics(cameraDevice.id) 353 val config = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! 354 val jpegSize = config.getOutputSizes(ImageFormat.JPEG)[0] 355 val imageReader = 356 ImageReader.newInstance(jpegSize.width, jpegSize.height, ImageFormat.JPEG, 1).apply { 357 setOnImageAvailableListener( 358 object : ImageReader.OnImageAvailableListener { 359 override fun onImageAvailable(imageReader: ImageReader) { 360 imageReader.acquireNextImage().close() 361 } 362 }, 363 captureHandler) 364 } 365 val outputConfiguration = OutputConfiguration(imageReader.surface) 366 val sessionConfiguration = 367 SessionConfiguration( 368 SessionConfiguration.SESSION_REGULAR, 369 listOf(outputConfiguration), 370 cameraExecutor, 371 object : CameraCaptureSession.StateCallback() { 372 override fun onConfigured(session: CameraCaptureSession) { 373 Log.v(TAG, "CaptureCaptureSession.StateCallback.onConfigured()") 374 continuation.tryOrResume(keys, result, "openCamera2Stream/onConfigured") { 375 captureCamera2( 376 cameraDevice, 377 session, 378 result, 379 continuation, 380 imageReader.surface, 381 shouldRepeat, 382 captureHandler) 383 } 384 } 385 386 override fun onConfigureFailed(session: CameraCaptureSession) { 387 Log.v(TAG, "CaptureCaptureSession.StateCallback.onConfigureFailed()") 388 session.close() 389 cameraDevice.close() 390 continuation.resume(signalStreamOpened(false, result)) 391 } 392 393 override fun onReady(session: CameraCaptureSession) { 394 Log.v(TAG, "CaptureCaptureSession.StateCallback.onReady()") 395 } 396 }) 397 cameraDevice.createCaptureSession(sessionConfiguration) 398 } 399 400 private fun captureCamera2( 401 cameraDevice: CameraDevice, 402 session: CameraCaptureSession, 403 result: Intent, 404 continuation: CancellableContinuation<Intent>, 405 target: Surface, 406 shouldRepeat: Boolean, 407 captureHandler: Handler 408 ) { 409 val cleanup: () -> Unit = { 410 Log.v(TAG, "captureCamera2/cleanup") 411 continuation.tryOrResume(keys, result, "captureCamera2/cleanup") { 412 session.stopRepeating() 413 session.close() 414 cameraDevice.close() 415 } 416 } 417 418 val captureRequest = 419 cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).let { 420 it.addTarget(target) 421 it.build() 422 } 423 424 val captureCallback = 425 object : CameraCaptureSession.CaptureCallback() { 426 private var firstCaptureCompleted = false 427 428 override fun onCaptureCompleted( 429 session: CameraCaptureSession, 430 request: CaptureRequest, 431 captureResult: TotalCaptureResult, 432 ) { 433 if (!firstCaptureCompleted) { 434 Log.v(TAG, "CameraCaptureSession.CaptureCallback.onCaptureCompleted() (first)") 435 onStopRepeating = { 436 Log.v(TAG, "onStopRepeating") 437 cleanup() 438 439 if (continuation.isActive) { 440 continuation.resume(result.apply { putExtra(keys.stoppedRepeating, true) }) 441 } 442 } 443 444 signalStreamOpened(true, result) 445 firstCaptureCompleted = true 446 } else { 447 Log.v(TAG, "CameraCaptureSession.CaptureCallback.onCaptureCompleted()") 448 } 449 450 if (!shouldRepeat) { 451 cleanup() 452 continuation.resume(result) 453 } 454 } 455 456 override fun onCaptureFailed( 457 session: CameraCaptureSession, 458 request: CaptureRequest, 459 failure: CaptureFailure 460 ) { 461 Log.v(TAG, "CameraCaptureSession.CaptureCallback.onCaptureFailed()") 462 } 463 } 464 465 if (shouldRepeat) { 466 Log.v(TAG, "captureCamera2: setRepeatingRequest") 467 session.setRepeatingRequest(captureRequest, captureCallback, captureHandler) 468 } else { 469 Log.v(TAG, "captureCamera2: capture") 470 session.capture(captureRequest, captureCallback, captureHandler) 471 } 472 } 473 474 private fun openCameraNdkStream( 475 result: Intent, 476 continuation: CancellableContinuation<Intent>, 477 shouldRepeat: Boolean 478 ) { 479 Log.v(TAG, "openCameraNdkStream: shouldRepeat ${shouldRepeat}") 480 val openCameraStreamResult = nativeOpenCameraStream(shouldRepeat) 481 if (openCameraStreamResult != 0) { 482 signalStreamOpened(false, result) 483 Log.e(TAG, "Failed to open camera stream: ${openCameraStreamResult}") 484 continuation.resume(result.apply { putExtra(keys.error, openCameraStreamResult) }) 485 return 486 } 487 488 signalStreamOpened(true, result) 489 if (shouldRepeat) { 490 onStopRepeating = { 491 Log.v(TAG, "onStopRepeating") 492 result.putExtra(keys.stoppedRepeating, true) 493 nativeStopRepeating() 494 } 495 496 val stopRepeatingResult = nativeWaitStopRepeating() 497 result.putExtra(keys.error, stopRepeatingResult) 498 } 499 continuation.resume(result) 500 } 501 502 private fun signalStreamOpened(streamOpened: Boolean, result: Intent): Intent { 503 val streamOpenedIntent = 504 Intent(keys.streamOpened).apply { putExtra(keys.streamOpened, streamOpened) } 505 streamOpenedIntent.setPackage("android.security.cts") 506 context.sendBroadcast(streamOpenedIntent) 507 return result.apply { putExtra(keys.streamOpened, streamOpened) } 508 } 509 510 protected external fun nativeInit() 511 512 protected external fun nativeCleanup() 513 514 protected external fun nativeHasCamera(): Boolean 515 516 protected external fun nativeOpenCamera(): Int 517 518 protected external fun nativeOpenCameraStream(shouldRepeat: Boolean): Int 519 520 protected external fun nativeWaitStopRepeating(): Int 521 522 protected external fun nativeStopRepeating(): Int 523 524 private companion object { 525 val TAG = CameraOpener::class.java.simpleName 526 527 const val CAMERA_PROXY_APP_PACKAGE_NAME = "android.security.cts.camera.proxy" 528 const val CAMERA_PROXY_ACTIVITY = "$CAMERA_PROXY_APP_PACKAGE_NAME.CameraProxyActivity" 529 } 530 } 531