1 /* 2 * Copyright 2020 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.internal.compat; 18 19 import android.hardware.camera2.CameraCharacteristics; 20 import android.hardware.camera2.CameraMetadata; 21 import android.hardware.camera2.params.StreamConfigurationMap; 22 import android.os.Build; 23 24 import androidx.annotation.GuardedBy; 25 import androidx.annotation.IntRange; 26 import androidx.annotation.VisibleForTesting; 27 import androidx.camera.camera2.internal.compat.workaround.OutputSizesCorrector; 28 29 import org.jspecify.annotations.NonNull; 30 import org.jspecify.annotations.Nullable; 31 32 import java.util.HashMap; 33 import java.util.Map; 34 import java.util.Set; 35 36 /** 37 * A wrapper for {@link CameraCharacteristics} which caches the retrieved values to optimize 38 * the latency and might contain backward compatible fixes for certain parameters. 39 */ 40 public class CameraCharacteristicsCompat { 41 @GuardedBy("this") 42 private final @NonNull Map<CameraCharacteristics.Key<?>, Object> mValuesCache = new HashMap<>(); 43 private final @NonNull CameraCharacteristicsCompatImpl mCameraCharacteristicsImpl; 44 private final @NonNull String mCameraId; 45 46 private @Nullable StreamConfigurationMapCompat mStreamConfigurationMapCompat = null; 47 CameraCharacteristicsCompat(@onNull CameraCharacteristics cameraCharacteristics, @NonNull String cameraId)48 private CameraCharacteristicsCompat(@NonNull CameraCharacteristics cameraCharacteristics, 49 @NonNull String cameraId) { 50 if (Build.VERSION.SDK_INT >= 28) { 51 mCameraCharacteristicsImpl = new CameraCharacteristicsApi28Impl(cameraCharacteristics); 52 } else { 53 mCameraCharacteristicsImpl = new CameraCharacteristicsBaseImpl(cameraCharacteristics); 54 } 55 mCameraId = cameraId; 56 } 57 58 /** 59 * Tests might need to create CameraCharacteristicsCompat directly for convenience. Elsewhere 60 * we should get the CameraCharacteristicsCompat instance from {@link CameraManagerCompat}. 61 */ 62 @VisibleForTesting toCameraCharacteristicsCompat( @onNull CameraCharacteristics characteristics, @NonNull String cameraId)63 public static @NonNull CameraCharacteristicsCompat toCameraCharacteristicsCompat( 64 @NonNull CameraCharacteristics characteristics, @NonNull String cameraId) { 65 return new CameraCharacteristicsCompat(characteristics, cameraId); 66 } 67 68 /** 69 * Return true if the key should be retrieved from {@link CameraCharacteristics} without 70 * caching it. 71 */ isKeyNonCacheable(CameraCharacteristics.@onNull Key<?> key)72 private boolean isKeyNonCacheable(CameraCharacteristics.@NonNull Key<?> key) { 73 // SENSOR_ORIENTATION value should change in some circumstances. 74 return key.equals(CameraCharacteristics.SENSOR_ORIENTATION); 75 } 76 77 /** 78 * Gets a camera characteristics field value and caches the value for later use. 79 * 80 * <p>It will cache the value once get() is called. If get() is called more than once using 81 * the same key, it will return instantly. 82 * 83 * @param key The characteristics field to read. 84 * @return The value of that key, or null if the field is not set. 85 */ get(CameraCharacteristics.@onNull Key<T> key)86 public <T> @Nullable T get(CameraCharacteristics.@NonNull Key<T> key) { 87 // For some keys that will have varying value and cannot be cached, we need to always 88 // retrieve the key from the CameraCharacteristics. 89 if (isKeyNonCacheable(key)) { 90 return mCameraCharacteristicsImpl.get(key); 91 } 92 93 synchronized (this) { 94 @SuppressWarnings("unchecked") // The value type always matches the key type. 95 T value = (T) mValuesCache.get(key); 96 if (value != null) { 97 return value; 98 } 99 100 value = mCameraCharacteristicsImpl.get(key); 101 if (value != null) { 102 mValuesCache.put(key, value); 103 } 104 return value; 105 } 106 } 107 108 /** 109 * Returns the physical camera Ids if it is a logical camera. Otherwise it would 110 * return an empty set. 111 */ getPhysicalCameraIds()112 public @NonNull Set<String> getPhysicalCameraIds() { 113 return mCameraCharacteristicsImpl.getPhysicalCameraIds(); 114 } 115 116 /** 117 * Returns {@code true} if overriding zoom setting is available, otherwise {@code false}. 118 */ isZoomOverrideAvailable()119 public boolean isZoomOverrideAvailable() { 120 if (Build.VERSION.SDK_INT >= 34) { 121 int[] availableSettingsOverrides = mCameraCharacteristicsImpl.get( 122 CameraCharacteristics.CONTROL_AVAILABLE_SETTINGS_OVERRIDES); 123 if (availableSettingsOverrides != null) { 124 for (int i : availableSettingsOverrides) { 125 if (i == CameraMetadata.CONTROL_SETTINGS_OVERRIDE_ZOOM) { 126 return true; 127 } 128 } 129 } 130 } 131 return false; 132 } 133 134 /** 135 * Returns the default torch strength level. 136 */ getDefaultTorchStrengthLevel()137 public int getDefaultTorchStrengthLevel() { 138 Integer defaultLevel = null; 139 if (hasFlashUnit() && Build.VERSION.SDK_INT >= 35) { 140 defaultLevel = get(CameraCharacteristics.FLASH_TORCH_STRENGTH_DEFAULT_LEVEL); 141 } 142 // The framework returns 1 when the device doesn't support configuring torch strength. So 143 // also return 1 if the device doesn't have flash unit or is unable to provide the 144 // information. 145 return defaultLevel == null ? 1 : defaultLevel; 146 } 147 148 /** 149 * Returns the maximum torch strength level. 150 */ 151 @IntRange(from = 1) getMaxTorchStrengthLevel()152 public int getMaxTorchStrengthLevel() { 153 Integer maxLevel = null; 154 if (hasFlashUnit() && Build.VERSION.SDK_INT >= 35) { 155 maxLevel = get(CameraCharacteristics.FLASH_TORCH_STRENGTH_MAX_LEVEL); 156 } 157 // The framework returns 1 when the device doesn't support configuring torch strength. So 158 // also return 1 if the device doesn't have flash unit or is unable to provide the 159 // information. 160 return maxLevel == null ? 1 : maxLevel; 161 } 162 isTorchStrengthLevelSupported()163 public boolean isTorchStrengthLevelSupported() { 164 return hasFlashUnit() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM 165 && getMaxTorchStrengthLevel() > 1; 166 } 167 hasFlashUnit()168 private boolean hasFlashUnit() { 169 Boolean flashInfoAvailable = get(CameraCharacteristics.FLASH_INFO_AVAILABLE); 170 return flashInfoAvailable != null && flashInfoAvailable; 171 } 172 173 /** 174 * Obtains the {@link StreamConfigurationMapCompat} which contains the output sizes related 175 * workarounds in it. 176 */ getStreamConfigurationMapCompat()177 public @NonNull StreamConfigurationMapCompat getStreamConfigurationMapCompat() { 178 if (mStreamConfigurationMapCompat == null) { 179 StreamConfigurationMap map; 180 try { 181 map = get( 182 CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 183 } catch (NullPointerException | AssertionError e) { 184 // Some devices may throw AssertionError when querying stream configuration map 185 // from CameraCharacteristics during bindToLifecycle. Catch the AssertionError and 186 // throw IllegalArgumentException so app level can decide how to handle. 187 throw new IllegalArgumentException(e.getMessage()); 188 } 189 if (map == null) { 190 throw new IllegalArgumentException("StreamConfigurationMap is null!"); 191 } 192 OutputSizesCorrector outputSizesCorrector = new OutputSizesCorrector(mCameraId); 193 mStreamConfigurationMapCompat = 194 StreamConfigurationMapCompat.toStreamConfigurationMapCompat(map, 195 outputSizesCorrector); 196 } 197 198 return mStreamConfigurationMapCompat; 199 } 200 201 /** 202 * Returns the {@link CameraCharacteristics} represented by this object. 203 */ toCameraCharacteristics()204 public @NonNull CameraCharacteristics toCameraCharacteristics() { 205 return mCameraCharacteristicsImpl.unwrap(); 206 } 207 208 /** 209 * Returns the camera id associated with the camera characteristics. 210 */ getCameraId()211 public @NonNull String getCameraId() { 212 return mCameraId; 213 } 214 215 /** 216 * CameraCharacteristic Implementation Interface 217 */ 218 public interface CameraCharacteristicsCompatImpl { 219 /** 220 * Gets the key/values from the CameraCharacteristics. 221 */ get(CameraCharacteristics.@onNull Key<T> key)222 <T> @Nullable T get(CameraCharacteristics.@NonNull Key<T> key); 223 224 /** 225 * Gets physical camera ids. 226 */ getPhysicalCameraIds()227 @NonNull Set<String> getPhysicalCameraIds(); 228 229 /** 230 * Returns the underlying {@link CameraCharacteristics} instance. 231 */ unwrap()232 @NonNull CameraCharacteristics unwrap(); 233 } 234 }