blob: c488d93e4513d55a57065cb09a7dfda68a793415 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.camera2.pipe.impl
import android.content.Context
import android.hardware.camera2.CameraManager
import android.util.ArrayMap
import androidx.annotation.GuardedBy
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.CameraMetadata
import androidx.camera.camera2.pipe.impl.Timestamps.formatMs
import kotlinx.coroutines.withContext
import java.lang.IllegalStateException
import javax.inject.Inject
import javax.inject.Singleton
/**
* Provides caching and querying of CameraMetadata.
*
* This class is designed to be thread safe and provides suspend functions for querying and
* accessing CameraMetadata.
*/
@Singleton
class CameraMetadataCache @Inject constructor(
private val context: Context,
private val threads: Threads,
private val permissions: Permissions
) {
@GuardedBy("cache")
private val cache = ArrayMap<String, CameraMetadata>()
suspend fun get(cameraId: CameraId): CameraMetadata {
synchronized(cache) {
val existing = cache[cameraId.value]
if (existing != null) {
return existing
}
}
// Suspend and query CameraMetadata on a background thread.
return withContext(threads.ioDispatcher) {
awaitMetadata(cameraId)
}
}
fun awaitMetadata(cameraId: CameraId): CameraMetadata {
return Debug.trace("awaitMetadata") {
synchronized(cache) {
val existing = cache[cameraId.value]
if (existing != null) {
return@trace existing
} else if (!isMetadataRedacted()) {
val result = createCameraMetadata(cameraId, false)
cache[cameraId.value] = result
return@trace result
}
}
return@trace createCameraMetadata(cameraId, true)
}
}
private fun createCameraMetadata(cameraId: CameraId, redacted: Boolean): CameraMetadataImpl {
val start = Timestamps.now()
return Debug.trace("CameraCharacteristics_$cameraId") {
try {
val cameraManager =
context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
val characteristics =
cameraManager.getCameraCharacteristics(cameraId.value)
val cameraMetadata =
CameraMetadataImpl(cameraId, redacted, characteristics, emptyMap())
Log.info {
val duration = Timestamps.now() - start
val redactedString = when (redacted) {
false -> ""
true -> " (redacted)"
}
"Loaded metadata for $cameraId in ${duration.formatMs()}$redactedString"
}
return@trace cameraMetadata
} catch (e: Throwable) {
throw IllegalStateException("Failed to load metadata for $cameraId", e)
}
}
}
private fun isMetadataRedacted(): Boolean = !permissions.hasCameraPermission
}