From 39e5172306e4d1280c9b55366b1925fc6b5a1009 Mon Sep 17 00:00:00 2001 From: Austin Borger Date: Wed, 8 Jun 2022 16:45:37 -0700 Subject: [PATCH] Unhide some camera2 data class constructors to tests. Currently, if apps want to make functionality involving these classes testable, they need to copy contents out of them / use proxy classes / wrappers, since they cannot create instances of these (their constructors are hidden), and also cannot mock them (they're final). There is already precedent for keeping constructors of similar classes available (see e.g. TonemapCurve or RggbChannelVector). Un-hiding more of them would make the API more consistent, easier to test, and I'm not aware of any major downsides of doing so. We might also consider unhiding some more of them (e.g. ones about stream configurations) in the future; these four seem to be the easiest choices though. (We're also adding some NonNull / Nullable annotations to make lint happy.) Test: Ran GCA Bug: 202059787 Change-Id: Ie143d1b349e15510fd4d0058dfa9077a613836e4 --- core/api/current.txt | 5 ++ .../camera2/params/BlackLevelPattern.java | 8 +- .../hardware/camera2/params/Capability.java | 9 +- .../DeviceStateSensorOrientationMap.java | 16 +++- .../android/hardware/camera2/params/Face.java | 90 +++++++++++-------- 5 files changed, 85 insertions(+), 43 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 58f7fdbfa373f..e880ce5c27438 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -18147,12 +18147,14 @@ package android.hardware.camera2 { package android.hardware.camera2.params { public final class BlackLevelPattern { + ctor public BlackLevelPattern(@NonNull int[]); method public void copyTo(int[], int); method public int getOffsetForIndex(int, int); field public static final int COUNT = 4; // 0x4 } public final class Capability { + ctor public Capability(int, int, int, float, float); method @NonNull public android.util.Size getMaxStreamingSize(); method public int getMode(); method @NonNull public android.util.Range getZoomRatioRange(); @@ -18167,6 +18169,7 @@ package android.hardware.camera2.params { } public final class DeviceStateSensorOrientationMap { + ctor public DeviceStateSensorOrientationMap(@NonNull long[]); method public int getSensorOrientation(long); field public static final long FOLDED = 4L; // 0x4L field public static final long NORMAL = 0L; // 0x0L @@ -18201,6 +18204,8 @@ package android.hardware.camera2.params { } public final class Face { + ctor public Face(@NonNull android.graphics.Rect, int, int, @NonNull android.graphics.Point, @NonNull android.graphics.Point, @NonNull android.graphics.Point); + ctor public Face(@NonNull android.graphics.Rect, int); method public android.graphics.Rect getBounds(); method public int getId(); method public android.graphics.Point getLeftEyePosition(); diff --git a/core/java/android/hardware/camera2/params/BlackLevelPattern.java b/core/java/android/hardware/camera2/params/BlackLevelPattern.java index 4405df8c8a2bf..02386bf01c740 100644 --- a/core/java/android/hardware/camera2/params/BlackLevelPattern.java +++ b/core/java/android/hardware/camera2/params/BlackLevelPattern.java @@ -16,6 +16,7 @@ package android.hardware.camera2.params; +import android.annotation.NonNull; import android.annotation.Nullable; import java.util.Arrays; @@ -43,13 +44,16 @@ public final class BlackLevelPattern { * a 2x2 pattern corresponding to the color filter arrangement. Offsets are * given in row-column scan order.

* + *

This constructor is public to allow for easier application testing by + * creating custom object instances. It's not necessary to construct these + * objects during normal use of the camera API.

+ * * @param offsets an array containing a 2x2 pattern of offsets. * * @throws IllegalArgumentException if the given array has an incorrect length. * @throws NullPointerException if the given array is null. - * @hide */ - public BlackLevelPattern(int[] offsets) { + public BlackLevelPattern(@NonNull int[] offsets) { if (offsets == null) { throw new NullPointerException("Null offsets array passed to constructor"); } diff --git a/core/java/android/hardware/camera2/params/Capability.java b/core/java/android/hardware/camera2/params/Capability.java index ebb534a489556..fd71c82817f53 100644 --- a/core/java/android/hardware/camera2/params/Capability.java +++ b/core/java/android/hardware/camera2/params/Capability.java @@ -48,6 +48,14 @@ public final class Capability { /** * Create a new Capability object. * + *

The mode argument can be any integer value. maxStreamingWidth and maxStreamingHeight + * must be non-negative, while minZoomRatio and maxZoomRatio must be strictly + * positive.

+ * + *

This constructor is public to allow for easier application testing by + * creating custom object instances. It's not necessary to construct these + * objects during normal use of the camera API.

+ * * @param mode supported mode for a camera capability. * @param maxStreamingWidth The width of the maximum streaming size for this mode * @param maxStreamingHeight The height of the maximum streaming size for this mode @@ -55,7 +63,6 @@ public final class Capability { * @param maxZoomRatio the maximum zoom ratio this mode supports * * @throws IllegalArgumentException if any of the argument is not valid - * @hide */ public Capability(int mode, int maxStreamingWidth, int maxStreamingHeight, float minZoomRatio, float maxZoomRatio) { diff --git a/core/java/android/hardware/camera2/params/DeviceStateSensorOrientationMap.java b/core/java/android/hardware/camera2/params/DeviceStateSensorOrientationMap.java index 200409e9e0004..c4c8eb9ef6f0f 100644 --- a/core/java/android/hardware/camera2/params/DeviceStateSensorOrientationMap.java +++ b/core/java/android/hardware/camera2/params/DeviceStateSensorOrientationMap.java @@ -17,6 +17,7 @@ package android.hardware.camera2.params; import android.annotation.LongDef; +import android.annotation.NonNull; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.utils.HashCodeHelpers; @@ -72,8 +73,19 @@ public final class DeviceStateSensorOrientationMap { /** * Create a new immutable DeviceStateOrientationMap instance. * + *

The array is a list of pairs of elements (angle, deviceState):

+ * + * [angle0, state0, angle1, state1,..., angleN, stateN] + * + *

Each pair describes the camera sensor orientation when the device is in the + * matching deviceState. The angle is in degrees, and must be a multiple of 90.

+ * *

This constructor takes over the array; do not write to the array afterwards.

* + *

This constructor is public to allow for easier application testing by + * creating custom object instances. It's not necessary to construct these + * objects during normal use of the camera API.

+ * * @param elements * An array of elements describing the map * @@ -82,10 +94,8 @@ public final class DeviceStateSensorOrientationMap { * invalid element values * @throws NullPointerException * if {@code elements} is {@code null} - * - * @hide */ - public DeviceStateSensorOrientationMap(final long[] elements) { + public DeviceStateSensorOrientationMap(@NonNull final long[] elements) { mElements = Objects.requireNonNull(elements, "elements must not be null"); if ((elements.length % 2) != 0) { throw new IllegalArgumentException("Device state sensor orientation map length " + diff --git a/core/java/android/hardware/camera2/params/Face.java b/core/java/android/hardware/camera2/params/Face.java index e94f11d8a0566..fa1202b87efe9 100644 --- a/core/java/android/hardware/camera2/params/Face.java +++ b/core/java/android/hardware/camera2/params/Face.java @@ -17,6 +17,8 @@ package android.hardware.camera2.params; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; import android.hardware.camera2.CameraCharacteristics; @@ -49,12 +51,12 @@ public final class Face { */ public static final int SCORE_MAX = 100; - private final Rect mBounds; - private final int mScore; - private final int mId; - private final Point mLeftEye; - private final Point mRightEye; - private final Point mMouth; + private Rect mBounds; + private int mScore; + private int mId; + private Point mLeftEye; + private Point mRightEye; + private Point mMouth; /** * Create a new face with all fields set. @@ -67,6 +69,10 @@ public final class Face { * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, * rightEyePosition, and mouthPosition may be independently null or not-null.

* + *

This constructor is public to allow for easier application testing by + * creating custom object instances. It's not necessary to construct these + * objects during normal use of the camera API.

+ * * @param bounds Bounds of the face. * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}. * @param id A unique ID per face visible to the tracker. @@ -81,11 +87,48 @@ public final class Face { * or if id is {@value #ID_UNSUPPORTED} and * leftEyePosition/rightEyePosition/mouthPosition aren't all null, * or else if id is negative. - * - * @hide */ - public Face(Rect bounds, int score, int id, - Point leftEyePosition, Point rightEyePosition, Point mouthPosition) { + public Face(@NonNull Rect bounds, int score, int id, + @NonNull Point leftEyePosition, @NonNull Point rightEyePosition, + @NonNull Point mouthPosition) { + init(bounds, score, id, leftEyePosition, rightEyePosition, mouthPosition); + } + + /** + * Create a new face without the optional fields. + * + *

The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional. + * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and + * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, + * rightEyePosition, and mouthPosition may be independently null or not-null. When devices + * report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as + * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link CaptureResult}, + * the face id of each face is expected to be {@value #ID_UNSUPPORTED}, the leftEyePosition, + * rightEyePosition, and mouthPositions are expected to be {@code null} for each face.

+ * + *

This constructor is public to allow for easier application testing by + * creating custom object instances. It's not necessary to construct these + * objects during normal use of the camera API.

+ * + * @param bounds Bounds of the face. + * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}. + * + * @throws IllegalArgumentException + * if bounds is {@code null}, + * or if the confidence is not in the range of + * {@value #SCORE_MIN}-{@value #SCORE_MAX}. + */ + public Face(@NonNull Rect bounds, int score) { + init(bounds, score, ID_UNSUPPORTED, + /*leftEyePosition*/null, /*rightEyePosition*/null, /*mouthPosition*/null); + } + + /** + * Initialize the object (shared by constructors). + */ + private void init(@NonNull Rect bounds, int score, int id, + @Nullable Point leftEyePosition, @Nullable Point rightEyePosition, + @Nullable Point mouthPosition) { checkNotNull("bounds", bounds); if (score < SCORE_MIN || score > SCORE_MAX) { throw new IllegalArgumentException("Confidence out of range"); @@ -106,33 +149,6 @@ public final class Face { mMouth = mouthPosition; } - /** - * Create a new face without the optional fields. - * - *

The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional. - * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and - * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, - * rightEyePosition, and mouthPosition may be independently null or not-null. When devices - * report the value of key {@link CaptureResult#STATISTICS_FACE_DETECT_MODE} as - * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_SIMPLE} in {@link CaptureResult}, - * the face id of each face is expected to be {@value #ID_UNSUPPORTED}, the leftEyePosition, - * rightEyePosition, and mouthPositions are expected to be {@code null} for each face.

- * - * @param bounds Bounds of the face. - * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}. - * - * @throws IllegalArgumentException - * if bounds is {@code null}, - * or if the confidence is not in the range of - * {@value #SCORE_MIN}-{@value #SCORE_MAX}. - * - * @hide - */ - public Face(Rect bounds, int score) { - this(bounds, score, ID_UNSUPPORTED, - /*leftEyePosition*/null, /*rightEyePosition*/null, /*mouthPosition*/null); - } - /** * Bounds of the face. *