xref: /aosp_15_r20/cts/tests/tests/sensorprivacy/test-apps/utils/src/android/sensorprivacy/cts/testapp/utils/Utils.kt (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)

<lambda>null1 package android.sensorprivacy.cts.testapp.utils
2 
3 import android.app.AppOpsManager
4 import android.content.Context
5 import android.hardware.camera2.CameraCaptureSession
6 import android.hardware.camera2.CameraCharacteristics
7 import android.hardware.camera2.CameraDevice
8 import android.hardware.camera2.CameraManager
9 import android.hardware.camera2.params.OutputConfiguration
10 import android.hardware.camera2.params.SessionConfiguration
11 import android.media.AudioFormat
12 import android.media.AudioRecord
13 import android.media.ImageReader
14 import android.media.MediaRecorder
15 import android.os.Handler
16 import android.os.HandlerThread
17 import android.os.Process
18 import android.util.Log
19 import android.util.Size
20 import java.util.concurrent.CompletableFuture
21 import java.util.concurrent.Executor
22 
23 private const val SAMPLING_RATE = 8000
24 
25 private const val RETRY_TIMEOUT = 5000L
26 private const val TAG = "SensorPrivacyTestAppUtils"
27 
28 fun openMic(): Mic {
29     val audioRecord = AudioRecord.Builder()
30             .setAudioFormat(AudioFormat.Builder()
31                     .setSampleRate(SAMPLING_RATE)
32                     .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
33                     .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build())
34             .setAudioSource(MediaRecorder.AudioSource.DEFAULT)
35             .setBufferSizeInBytes(
36                     AudioRecord.getMinBufferSize(SAMPLING_RATE,
37                             AudioFormat.CHANNEL_IN_MONO,
38                             AudioFormat.ENCODING_PCM_16BIT) * 10)
39             .build()
40 
41     audioRecord?.startRecording()
42 
43     return Mic(audioRecord)
44 }
45 
openCamnull46 fun openCam(context: Context): Cam {
47     return openCam(context, false)
48 }
49 
openCamnull50 fun openCam(context: Context, retryCam: Boolean): Cam {
51     return openCam(context, retryCam, 0, 0)
52 }
53 
openCamnull54 fun openCam(
55     context: Context,
56     retryCam: Boolean,
57     cameraOpenRetryCountRO: Int,
58     cameraMaxOpenRetryRO: Int
59 ): Cam {
60     var cameraOpenRetryCount = cameraOpenRetryCountRO
61     var cameraMaxOpenRetry = cameraMaxOpenRetryRO
62 
63     val cameraManager = context.getSystemService(CameraManager::class.java)!!
64     val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
65 
66     val cameraId = cameraManager!!.cameraIdList[0]
67     val config = cameraManager!!.getCameraCharacteristics(cameraId)
68             .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
69     val outputFormat = config!!.outputFormats[0]
70     val outputSize: Size = config!!.getOutputSizes(outputFormat)[0]
71     val handlerThread = HandlerThread("CameraThread")
72     handlerThread.start()
73     val handler = Handler(handlerThread.looper)
74     val executor = Executor {
75         runnable ->
76         run {
77             handler.post(runnable)
78         }
79     }
80 
81     var cameraDevice: CompletableFuture<CameraDevice> = CompletableFuture()
82 
83     // Retry camera connection because external cameras are disconnected
84     // if sensor privacy is enabled (b/182204067)
85     val isExternalCamera = (cameraManager!!.getCameraCharacteristics(cameraId)
86             .get(CameraCharacteristics.LENS_FACING)
87             == CameraCharacteristics.LENS_FACING_EXTERNAL)
88     if (retryCam && isExternalCamera) {
89         cameraMaxOpenRetry = 1
90     }
91 
92     val cameraDeviceCallback = object : CameraDevice.StateCallback() {
93         override fun onOpened(cD: CameraDevice) {
94             val imageReader = ImageReader.newInstance(
95                     outputSize.width, outputSize.height, outputFormat, 2)
96 
97             val builder = cD.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
98             builder.addTarget(imageReader.surface)
99             val captureRequest = builder.build()
100             val sessionConfiguration = SessionConfiguration(
101                     SessionConfiguration.SESSION_REGULAR,
102                     listOf(OutputConfiguration(imageReader.surface)),
103                     executor,
104                     object : CameraCaptureSession.StateCallback() {
105                         override fun onConfigured(session: CameraCaptureSession) {
106                             session.capture(captureRequest, null, handler)
107                             appOpsManager.startOpNoThrow(AppOpsManager.OPSTR_CAMERA,
108                                     Process.myUid(), context.opPackageName)
109                         }
110 
111                         override fun onConfigureFailed(session: CameraCaptureSession) {}
112                     })
113 
114             cD.createCaptureSession(sessionConfiguration)
115             cameraDevice.complete(cD)
116             cameraOpenRetryCount = 0
117         }
118 
119         override fun onDisconnected(cD: CameraDevice) {
120         }
121 
122         override fun onError(cD: CameraDevice, i: Int) {
123             // Retry once after timeout if cause is ERROR_CAMERA_DISABLED because it may
124             // be triggered if camera mute is not supported and sensor privacy is enabled
125             if (i == ERROR_CAMERA_DISABLED && cameraOpenRetryCount < cameraMaxOpenRetry) {
126                 cD.close()
127                 cameraOpenRetryCount++
128                 cameraDevice.complete(cD)
129                 handler.postDelayed({
130                     openCam(context, retryCam, cameraOpenRetryCount, cameraMaxOpenRetry)
131                 }, RETRY_TIMEOUT)
132             }
133         }
134     }
135 
136     try {
137         cameraManager!!.openCamera(cameraId, executor, cameraDeviceCallback)
138     } catch (e: android.hardware.camera2.CameraAccessException) {
139         Log.e(TAG, "openCamera: " + e)
140     }
141 
142     return Cam(cameraDevice.join(), handlerThread)
143 }
144 
145 class Mic(val audioRecord: AudioRecord) {
closenull146     fun close() {
147         audioRecord.stop()
148     }
149 }
150 
151 class Cam(val cameraDevice: CameraDevice?, val thread: Thread) {
closenull152     fun close() {
153         cameraDevice?.close()
154         thread.interrupt()
155     }
156 }