1 /* 2 * Copyright (C) 2019 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.testing.fakes; 18 19 import static androidx.camera.core.DynamicRange.SDR; 20 21 import android.content.Context; 22 import android.hardware.camera2.CameraAccessException; 23 import android.hardware.camera2.CameraManager; 24 import android.util.Range; 25 import android.util.Rational; 26 import android.util.Size; 27 import android.view.Surface; 28 29 import androidx.annotation.FloatRange; 30 import androidx.annotation.RestrictTo; 31 import androidx.camera.core.CameraSelector; 32 import androidx.camera.core.CameraState; 33 import androidx.camera.core.DynamicRange; 34 import androidx.camera.core.ExposureState; 35 import androidx.camera.core.FocusMeteringAction; 36 import androidx.camera.core.TorchState; 37 import androidx.camera.core.ZoomState; 38 import androidx.camera.core.impl.CameraCaptureCallback; 39 import androidx.camera.core.impl.CameraInfoInternal; 40 import androidx.camera.core.impl.DynamicRanges; 41 import androidx.camera.core.impl.EncoderProfilesProvider; 42 import androidx.camera.core.impl.ImageOutputConfig.RotationValue; 43 import androidx.camera.core.impl.Quirk; 44 import androidx.camera.core.impl.Quirks; 45 import androidx.camera.core.impl.Timebase; 46 import androidx.camera.core.impl.utils.CameraOrientationUtil; 47 import androidx.camera.core.internal.ImmutableZoomState; 48 import androidx.core.util.Preconditions; 49 import androidx.lifecycle.LiveData; 50 import androidx.lifecycle.MutableLiveData; 51 import androidx.test.core.app.ApplicationProvider; 52 53 import org.jspecify.annotations.NonNull; 54 import org.jspecify.annotations.Nullable; 55 56 import java.util.ArrayList; 57 import java.util.Arrays; 58 import java.util.Collections; 59 import java.util.HashMap; 60 import java.util.HashSet; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.Set; 64 import java.util.concurrent.Executor; 65 66 /** 67 * Fake implementation for retrieving camera information of a fake camera. 68 * 69 * <p>This camera info can be constructed with fake values. 70 */ 71 public final class FakeCameraInfoInternal implements CameraInfoInternal { 72 private static final Set<Range<Integer>> FAKE_FPS_RANGES = Collections.unmodifiableSet( 73 new HashSet<>(Arrays.asList( 74 new Range<>(12, 30), 75 new Range<>(30, 30), 76 new Range<>(60, 60)) 77 ) 78 ); 79 private static final Set<DynamicRange> DEFAULT_DYNAMIC_RANGES = Collections.singleton(SDR); 80 private final String mCameraId; 81 private final int mSensorRotation; 82 @CameraSelector.LensFacing 83 private final int mLensFacing; 84 private final MutableLiveData<Integer> mTorchState = new MutableLiveData<>(TorchState.OFF); 85 private final MutableLiveData<ZoomState> mZoomLiveData; 86 private final Map<Integer, List<Size>> mSupportedResolutionMap = new HashMap<>(); 87 private final Map<Range<Integer>, List<Size>> mSupportedHighSpeedFpsToSizeMap = new HashMap<>(); 88 private final Map<Integer, List<Size>> mSupportedHighResolutionMap = new HashMap<>(); 89 private MutableLiveData<CameraState> mCameraStateMutableLiveData; 90 91 private final Set<DynamicRange> mSupportedDynamicRanges = new HashSet<>(DEFAULT_DYNAMIC_RANGES); 92 private String mImplementationType = IMPLEMENTATION_TYPE_FAKE; 93 94 // Leave uninitialized to support camera-core:1.0.0 dependencies. 95 // Can be initialized during class init once there are no more pinned dependencies on 96 // camera-core:1.0.0 97 private EncoderProfilesProvider mEncoderProfilesProvider; 98 99 private boolean mIsPrivateReprocessingSupported = false; 100 private float mIntrinsicZoomRatio = 1.0F; 101 102 private boolean mIsFocusMeteringSupported = false; 103 private boolean mIsHighSpeedSupported = false; 104 105 private ExposureState mExposureState = new FakeExposureState(); 106 private final @NonNull List<Quirk> mCameraQuirks = new ArrayList<>(); 107 108 private Timebase mTimebase = Timebase.UPTIME; 109 110 private @Nullable CameraManager mCameraManager; 111 FakeCameraInfoInternal()112 public FakeCameraInfoInternal() { 113 this(/*sensorRotation=*/ 0, /*lensFacing=*/ CameraSelector.LENS_FACING_BACK); 114 } 115 116 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) FakeCameraInfoInternal(@onNull String cameraId, @NonNull Context context)117 public FakeCameraInfoInternal(@NonNull String cameraId, 118 @NonNull Context context) { 119 this(cameraId, 0, CameraSelector.LENS_FACING_BACK, context); 120 } 121 FakeCameraInfoInternal(@onNull String cameraId)122 public FakeCameraInfoInternal(@NonNull String cameraId) { 123 this(cameraId, 0, CameraSelector.LENS_FACING_BACK, 124 ApplicationProvider.getApplicationContext()); 125 } 126 FakeCameraInfoInternal(@onNull String cameraId, @CameraSelector.LensFacing int lensFacing)127 public FakeCameraInfoInternal(@NonNull String cameraId, 128 @CameraSelector.LensFacing int lensFacing) { 129 this(cameraId, 0, lensFacing, 130 ApplicationProvider.getApplicationContext()); 131 } 132 FakeCameraInfoInternal(int sensorRotation, @CameraSelector.LensFacing int lensFacing)133 public FakeCameraInfoInternal(int sensorRotation, @CameraSelector.LensFacing int lensFacing) { 134 this("0", sensorRotation, lensFacing, 135 ApplicationProvider.getApplicationContext()); 136 } 137 FakeCameraInfoInternal(@onNull String cameraId, int sensorRotation, @CameraSelector.LensFacing int lensFacing)138 public FakeCameraInfoInternal(@NonNull String cameraId, int sensorRotation, 139 @CameraSelector.LensFacing int lensFacing) { 140 this(cameraId, sensorRotation, lensFacing, 141 ApplicationProvider.getApplicationContext()); 142 } 143 144 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) FakeCameraInfoInternal(@onNull String cameraId, int sensorRotation, @CameraSelector.LensFacing int lensFacing, @NonNull Context context)145 public FakeCameraInfoInternal(@NonNull String cameraId, int sensorRotation, 146 @CameraSelector.LensFacing int lensFacing, 147 @NonNull Context context) { 148 mCameraId = cameraId; 149 mSensorRotation = sensorRotation; 150 mLensFacing = lensFacing; 151 mZoomLiveData = new MutableLiveData<>(ImmutableZoomState.create(1.0f, 4.0f, 1.0f, 0.0f)); 152 mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 153 } 154 155 /** 156 * Sets the zoom parameter. 157 */ setZoom(float zoomRatio, float minZoomRatio, float maxZoomRatio, float linearZoom)158 public void setZoom(float zoomRatio, float minZoomRatio, float maxZoomRatio, float linearZoom) { 159 mZoomLiveData.postValue(ImmutableZoomState.create( 160 zoomRatio, maxZoomRatio, minZoomRatio, linearZoom 161 )); 162 } 163 164 /** 165 * Sets the exposure compensation parameters. 166 */ setExposureState(int index, @NonNull Range<Integer> range, @NonNull Rational step, boolean isSupported)167 public void setExposureState(int index, @NonNull Range<Integer> range, 168 @NonNull Rational step, boolean isSupported) { 169 mExposureState = new FakeExposureState(index, range, step, isSupported); 170 } 171 172 /** 173 * Sets the torch state. 174 */ setTorch(int torchState)175 public void setTorch(int torchState) { 176 mTorchState.postValue(torchState); 177 } 178 179 /** 180 * Sets the return value for {@link #isFocusMeteringSupported(FocusMeteringAction)}. 181 */ setIsFocusMeteringSupported(boolean supported)182 public void setIsFocusMeteringSupported(boolean supported) { 183 mIsFocusMeteringSupported = supported; 184 } 185 186 @Override getLensFacing()187 public int getLensFacing() { 188 return mLensFacing; 189 } 190 191 @Override getCameraId()192 public @NonNull String getCameraId() { 193 return mCameraId; 194 } 195 196 @Override getSensorRotationDegrees(@otationValue int relativeRotation)197 public int getSensorRotationDegrees(@RotationValue int relativeRotation) { 198 int relativeRotationDegrees = 199 CameraOrientationUtil.surfaceRotationToDegrees(relativeRotation); 200 // Currently this assumes that a back-facing camera is always opposite to the screen. 201 // This may not be the case for all devices, so in the future we may need to handle that 202 // scenario. 203 Integer lensFacing = getLensFacing(); 204 boolean isOppositeFacingScreen = 205 lensFacing != null && (CameraSelector.LENS_FACING_BACK == getLensFacing()); 206 return CameraOrientationUtil.getRelativeImageRotation( 207 relativeRotationDegrees, 208 mSensorRotation, 209 isOppositeFacingScreen); 210 } 211 212 @Override getSensorRotationDegrees()213 public int getSensorRotationDegrees() { 214 return getSensorRotationDegrees(Surface.ROTATION_0); 215 } 216 217 @Override hasFlashUnit()218 public boolean hasFlashUnit() { 219 return true; 220 } 221 222 @Override getTorchState()223 public @NonNull LiveData<Integer> getTorchState() { 224 return mTorchState; 225 } 226 227 @Override getZoomState()228 public @NonNull LiveData<ZoomState> getZoomState() { 229 return mZoomLiveData; 230 } 231 232 @Override getExposureState()233 public @NonNull ExposureState getExposureState() { 234 return mExposureState; 235 } 236 getCameraStateMutableLiveData()237 private MutableLiveData<CameraState> getCameraStateMutableLiveData() { 238 if (mCameraStateMutableLiveData == null) { 239 mCameraStateMutableLiveData = new MutableLiveData<>( 240 CameraState.create(CameraState.Type.CLOSED)); 241 } 242 return mCameraStateMutableLiveData; 243 } 244 245 @Override getCameraState()246 public @NonNull LiveData<CameraState> getCameraState() { 247 return getCameraStateMutableLiveData(); 248 } 249 250 @Override getImplementationType()251 public @NonNull String getImplementationType() { 252 return mImplementationType; 253 } 254 255 @Override getEncoderProfilesProvider()256 public @NonNull EncoderProfilesProvider getEncoderProfilesProvider() { 257 return mEncoderProfilesProvider == null ? EncoderProfilesProvider.EMPTY : 258 mEncoderProfilesProvider; 259 } 260 261 @Override getTimebase()262 public @NonNull Timebase getTimebase() { 263 return mTimebase; 264 } 265 266 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 267 @Override getSupportedOutputFormats()268 public @NonNull Set<Integer> getSupportedOutputFormats() { 269 return mSupportedResolutionMap.keySet(); 270 } 271 272 @Override getSupportedResolutions(int format)273 public @NonNull List<Size> getSupportedResolutions(int format) { 274 List<Size> resolutions = mSupportedResolutionMap.get(format); 275 return resolutions != null ? resolutions : Collections.emptyList(); 276 } 277 278 @Override getSupportedHighResolutions(int format)279 public @NonNull List<Size> getSupportedHighResolutions(int format) { 280 List<Size> resolutions = mSupportedHighResolutionMap.get(format); 281 return resolutions != null ? resolutions : Collections.emptyList(); 282 } 283 284 @Override getSupportedDynamicRanges()285 public @NonNull Set<DynamicRange> getSupportedDynamicRanges() { 286 return mSupportedDynamicRanges; 287 } 288 289 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 290 @Override isHighSpeedSupported()291 public boolean isHighSpeedSupported() { 292 return mIsHighSpeedSupported; 293 } 294 295 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 296 @NonNull 297 @Override getSupportedHighSpeedFrameRateRanges()298 public Set<Range<Integer>> getSupportedHighSpeedFrameRateRanges() { 299 return mSupportedHighSpeedFpsToSizeMap.keySet(); 300 } 301 302 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 303 @NonNull 304 @Override getSupportedHighSpeedFrameRateRangesFor(@onNull Size size)305 public Set<Range<Integer>> getSupportedHighSpeedFrameRateRangesFor(@NonNull Size size) { 306 Set<Range<Integer>> ranges = new HashSet<>(); 307 for (Map.Entry<Range<Integer>, List<Size>> entry : 308 mSupportedHighSpeedFpsToSizeMap.entrySet()) { 309 if (entry.getValue().contains(size)) { 310 ranges.add(entry.getKey()); 311 } 312 } 313 return ranges; 314 } 315 316 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 317 @NonNull 318 @Override getSupportedHighSpeedResolutions()319 public List<Size> getSupportedHighSpeedResolutions() { 320 Set<Size> resolutions = new HashSet<>(); 321 for (List<Size> sizes : mSupportedHighSpeedFpsToSizeMap.values()) { 322 resolutions.addAll(sizes); 323 } 324 return new ArrayList<>(resolutions); 325 } 326 327 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 328 @NonNull 329 @Override getSupportedHighSpeedResolutionsFor(@onNull Range<Integer> fpsRange)330 public List<Size> getSupportedHighSpeedResolutionsFor(@NonNull Range<Integer> fpsRange) { 331 List<Size> resolutions = mSupportedHighSpeedFpsToSizeMap.get(fpsRange); 332 return resolutions != null ? resolutions : Collections.emptyList(); 333 } 334 335 /** 336 * Returns the supported dynamic ranges of this camera from a set of candidate dynamic ranges. 337 * 338 * <p>The dynamic ranges which represent what the camera supports will come from the dynamic 339 * ranges set on {@link #setSupportedDynamicRanges(Set)}, or will consist of {@code {SDR}} if 340 * {@code setSupportedDynamicRanges(Set)} has not been called. In order to stay compliant 341 * with the API contract of 342 * {@link androidx.camera.core.CameraInfo#querySupportedDynamicRanges(Set)}, it is 343 * required that the {@link Set} provided to {@code setSupportedDynamicRanges(Set)} should 344 * always contain {@link DynamicRange#SDR} and should never contain under-specified dynamic 345 * ranges, such as {@link DynamicRange#UNSPECIFIED} and 346 * {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}. 347 * 348 * @see androidx.camera.core.CameraInfo#querySupportedDynamicRanges(Set) 349 */ 350 @Override querySupportedDynamicRanges( @onNull Set<DynamicRange> candidateDynamicRanges)351 public @NonNull Set<DynamicRange> querySupportedDynamicRanges( 352 @NonNull Set<DynamicRange> candidateDynamicRanges) { 353 return DynamicRanges.findAllPossibleMatches( 354 candidateDynamicRanges, getSupportedDynamicRanges()); 355 } 356 357 @Override addSessionCaptureCallback(@onNull Executor executor, @NonNull CameraCaptureCallback callback)358 public void addSessionCaptureCallback(@NonNull Executor executor, 359 @NonNull CameraCaptureCallback callback) { 360 throw new UnsupportedOperationException("Not Implemented"); 361 } 362 363 @Override removeSessionCaptureCallback(@onNull CameraCaptureCallback callback)364 public void removeSessionCaptureCallback(@NonNull CameraCaptureCallback callback) { 365 throw new UnsupportedOperationException("Not Implemented"); 366 } 367 368 @Override getCameraQuirks()369 public @NonNull Quirks getCameraQuirks() { 370 return new Quirks(mCameraQuirks); 371 } 372 373 @Override getSupportedFrameRateRanges()374 public @NonNull Set<Range<Integer>> getSupportedFrameRateRanges() { 375 return FAKE_FPS_RANGES; 376 } 377 378 @Override isFocusMeteringSupported(@onNull FocusMeteringAction action)379 public boolean isFocusMeteringSupported(@NonNull FocusMeteringAction action) { 380 return mIsFocusMeteringSupported; 381 } 382 383 @androidx.camera.core.ExperimentalZeroShutterLag 384 @Override isZslSupported()385 public boolean isZslSupported() { 386 return false; 387 } 388 389 @Override isPrivateReprocessingSupported()390 public boolean isPrivateReprocessingSupported() { 391 return mIsPrivateReprocessingSupported; 392 } 393 394 @FloatRange(from = 0, fromInclusive = false) 395 @Override getIntrinsicZoomRatio()396 public float getIntrinsicZoomRatio() { 397 return mIntrinsicZoomRatio; 398 } 399 400 @Override isPreviewStabilizationSupported()401 public boolean isPreviewStabilizationSupported() { 402 return false; 403 } 404 405 @Override isVideoStabilizationSupported()406 public boolean isVideoStabilizationSupported() { 407 return false; 408 } 409 410 /** Adds a quirk to the list of this camera's quirks. */ 411 @SuppressWarnings("unused") addCameraQuirk(final @NonNull Quirk quirk)412 public void addCameraQuirk(final @NonNull Quirk quirk) { 413 mCameraQuirks.add(quirk); 414 } 415 416 /** 417 * Updates the {@link CameraState} value to the {@code LiveData} provided by 418 * {@link #getCameraState()}. 419 * 420 * @param cameraState the camera state value to set. 421 */ 422 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) updateCameraState(@onNull CameraState cameraState)423 public void updateCameraState(@NonNull CameraState cameraState) { 424 getCameraStateMutableLiveData().postValue(cameraState); 425 } 426 427 /** 428 * Set the implementation type for testing 429 */ setImplementationType(@mplementationType @onNull String implementationType)430 public void setImplementationType(@ImplementationType @NonNull String implementationType) { 431 mImplementationType = implementationType; 432 } 433 434 /** Set the EncoderProfilesProvider for testing */ setEncoderProfilesProvider( @onNull EncoderProfilesProvider encoderProfilesProvider)435 public void setEncoderProfilesProvider( 436 @NonNull EncoderProfilesProvider encoderProfilesProvider) { 437 mEncoderProfilesProvider = Preconditions.checkNotNull(encoderProfilesProvider); 438 } 439 440 /** Set the timebase for testing */ setTimebase(@onNull Timebase timebase)441 public void setTimebase(@NonNull Timebase timebase) { 442 mTimebase = timebase; 443 } 444 445 /** Set the supported resolutions for testing */ setSupportedResolutions(int format, @NonNull List<Size> resolutions)446 public void setSupportedResolutions(int format, @NonNull List<Size> resolutions) { 447 mSupportedResolutionMap.put(format, resolutions); 448 } 449 450 /** Set the supported high resolutions for testing */ setSupportedHighResolutions(int format, @NonNull List<Size> resolutions)451 public void setSupportedHighResolutions(int format, @NonNull List<Size> resolutions) { 452 mSupportedHighResolutionMap.put(format, resolutions); 453 } 454 455 /** Sets the return value for {@link #isHighSpeedSupported()}}. */ 456 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) setHighSpeedSupported(boolean supported)457 public void setHighSpeedSupported(boolean supported) { 458 mIsHighSpeedSupported = supported; 459 } 460 461 /** Set the supported high speed resolutions for testing */ 462 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) setSupportedHighSpeedResolutions(@onNull Range<Integer> fps, @NonNull List<Size> resolutions)463 public void setSupportedHighSpeedResolutions(@NonNull Range<Integer> fps, 464 @NonNull List<Size> resolutions) { 465 mSupportedHighSpeedFpsToSizeMap.put(fps, resolutions); 466 } 467 468 /** Set the isPrivateReprocessingSupported flag for testing */ setPrivateReprocessingSupported(boolean supported)469 public void setPrivateReprocessingSupported(boolean supported) { 470 mIsPrivateReprocessingSupported = supported; 471 } 472 473 /** Adds a available view angle for testing. */ setIntrinsicZoomRatio(float zoomRatio)474 public void setIntrinsicZoomRatio(float zoomRatio) { 475 mIntrinsicZoomRatio = zoomRatio; 476 } 477 478 /** Set the supported dynamic ranges for testing */ setSupportedDynamicRanges(@onNull Set<DynamicRange> dynamicRanges)479 public void setSupportedDynamicRanges(@NonNull Set<DynamicRange> dynamicRanges) { 480 mSupportedDynamicRanges.clear(); 481 mSupportedDynamicRanges.addAll(dynamicRanges); 482 } 483 484 @Override 485 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) getCameraCharacteristics()486 public @NonNull Object getCameraCharacteristics() { 487 try { 488 return mCameraManager.getCameraCharacteristics(mCameraId); 489 } catch (CameraAccessException e) { 490 throw new IllegalStateException("can't get CameraCharacteristics", e); 491 } 492 } 493 494 @Override 495 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) getPhysicalCameraCharacteristics(@onNull String physicalCameraId)496 public @Nullable Object getPhysicalCameraCharacteristics(@NonNull String physicalCameraId) { 497 try { 498 return mCameraManager.getCameraCharacteristics(physicalCameraId); 499 } catch (CameraAccessException e) { 500 throw new IllegalStateException("can't get CameraCharacteristics", e); 501 } 502 } 503 504 static final class FakeExposureState implements ExposureState { 505 private int mIndex = 0; 506 private Range<Integer> mRange = new Range<>(0, 0); 507 private Rational mStep = Rational.ZERO; 508 private boolean mIsSupported = true; 509 FakeExposureState()510 FakeExposureState() { 511 } FakeExposureState(int index, Range<Integer> range, Rational step, boolean isSupported)512 FakeExposureState(int index, Range<Integer> range, 513 Rational step, boolean isSupported) { 514 mIndex = index; 515 mRange = range; 516 mStep = step; 517 mIsSupported = isSupported; 518 } 519 520 @Override getExposureCompensationIndex()521 public int getExposureCompensationIndex() { 522 return mIndex; 523 } 524 525 @Override getExposureCompensationRange()526 public @NonNull Range<Integer> getExposureCompensationRange() { 527 return mRange; 528 } 529 530 @Override getExposureCompensationStep()531 public @NonNull Rational getExposureCompensationStep() { 532 return mStep; 533 } 534 535 @Override isExposureCompensationSupported()536 public boolean isExposureCompensationSupported() { 537 return mIsSupported; 538 } 539 } 540 } 541