1 /*
2  * Copyright (C) 2021 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 android.hardware.camera2.params;
18 
19 import android.annotation.LongDef;
20 import android.hardware.camera2.CameraCharacteristics;
21 import android.hardware.camera2.utils.HashCodeHelpers;
22 
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.Objects;
28 
29 /**
30  * Immutable class that maps the device fold state to sensor orientation.
31  *
32  * <p>Some {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical}
33  * cameras on foldables can include physical sensors with different sensor orientation
34  * values. As a result, the values of the logical camera device can potentially change depending
35  * on the device fold state.</p>
36  *
37  * <p>The device fold state to sensor orientation map will contain information about the
38  * respective logical camera sensor orientation given a device state. Clients
39  * can query the mapping for all possible supported folded states.
40  *
41  * @see CameraCharacteristics#SENSOR_ORIENTATION
42  */
43 public final class DeviceStateSensorOrientationMap {
44     /**
45      *  Needs to be kept in sync with the HIDL/AIDL DeviceState
46      */
47 
48     /**
49      * The device is in its normal physical configuration. This is the default if the
50      * device does not support multiple different states.
51      */
52     public static final long NORMAL = 0;
53 
54     /**
55      * The device is folded.  If not set, the device is unfolded or does not
56      * support folding.
57      *
58      * The exact point when this status change happens during the folding
59      * operation is device-specific.
60      */
61     public static final long FOLDED = 1 << 2;
62 
63     /** @hide */
64     @Retention(RetentionPolicy.SOURCE)
65     @LongDef(prefix = {"DEVICE_STATE"}, value =
66             {NORMAL,
67              FOLDED })
68     public @interface DeviceState {};
69 
70     private final HashMap<Long, Integer> mDeviceStateOrientationMap = new HashMap<>();
71 
72     /**
73      * Create a new immutable DeviceStateOrientationMap instance.
74      *
75      * <p>This constructor takes over the array; do not write to the array afterwards.</p>
76      *
77      * @param elements
78      *          An array of elements describing the map
79      *
80      * @throws IllegalArgumentException
81      *            if the {@code elements} array length is invalid, not divisible by 2 or contains
82      *            invalid element values
83      * @throws NullPointerException
84      *            if {@code elements} is {@code null}
85      *
86      * @hide
87      */
DeviceStateSensorOrientationMap(final long[] elements)88     public DeviceStateSensorOrientationMap(final long[] elements) {
89         mElements = Objects.requireNonNull(elements, "elements must not be null");
90         if ((elements.length % 2) != 0) {
91             throw new IllegalArgumentException("Device state sensor orientation map length " +
92                     elements.length + " is not even!");
93         }
94 
95         for (int i = 0; i < elements.length; i += 2) {
96             if ((elements[i+1] % 90) != 0) {
97                 throw new IllegalArgumentException("Sensor orientation not divisible by 90: " +
98                         elements[i+1]);
99             }
100 
101             mDeviceStateOrientationMap.put(elements[i], Math.toIntExact(elements[i + 1]));
102         }
103     }
104 
105     /**
106      * Return the logical camera sensor orientation given a specific device fold state.
107      *
108      * @param deviceState Device fold state
109      *
110      * @return Valid {@link android.hardware.camera2.CameraCharacteristics#SENSOR_ORIENTATION} for
111      *         any supported device fold state
112      *
113      * @throws IllegalArgumentException if the given device state is invalid
114      */
getSensorOrientation(@eviceState long deviceState)115     public int getSensorOrientation(@DeviceState long deviceState) {
116         if (!mDeviceStateOrientationMap.containsKey(deviceState)) {
117             throw new IllegalArgumentException("Invalid device state: " + deviceState);
118         }
119 
120         return mDeviceStateOrientationMap.get(deviceState);
121     }
122 
123     /**
124      * Check if this DeviceStateSensorOrientationMap is equal to another
125      * DeviceStateSensorOrientationMap.
126      *
127      * <p>Two device state orientation maps are equal if and only if all of their elements are
128      * {@link Object#equals equal}.</p>
129      *
130      * @return {@code true} if the objects were equal, {@code false} otherwise
131      */
132     @Override
equals(final Object obj)133     public boolean equals(final Object obj) {
134         if (obj == null) {
135             return false;
136         }
137         if (this == obj) {
138             return true;
139         }
140         if (obj instanceof DeviceStateSensorOrientationMap) {
141             final DeviceStateSensorOrientationMap other = (DeviceStateSensorOrientationMap) obj;
142             return Arrays.equals(mElements, other.mElements);
143         }
144         return false;
145     }
146 
147     /**
148      * {@inheritDoc}
149      */
150     @Override
hashCode()151     public int hashCode() {
152         return HashCodeHelpers.hashCodeGeneric(mElements);
153     }
154 
155     private final long[] mElements;
156 }
157