<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 }