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.annotation.NonNull; 21 import android.annotation.SuppressLint; 22 import android.hardware.camera2.CameraCharacteristics; 23 import android.hardware.camera2.utils.HashCodeHelpers; 24 25 import java.lang.annotation.Retention; 26 import java.lang.annotation.RetentionPolicy; 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.HashMap; 30 import java.util.Objects; 31 32 /** 33 * Immutable class that maps the device fold state to sensor orientation. 34 * 35 * <p>Some {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical} 36 * cameras on foldables can include physical sensors with different sensor orientation 37 * values. As a result, the values of the logical camera device can potentially change depending 38 * on the device fold state.</p> 39 * 40 * <p>The device fold state to sensor orientation map will contain information about the 41 * respective logical camera sensor orientation given a device state. Clients 42 * can query the mapping for all possible supported folded states. 43 * 44 * @see CameraCharacteristics#SENSOR_ORIENTATION 45 */ 46 public final class DeviceStateSensorOrientationMap { 47 /** 48 * Needs to be kept in sync with the HIDL/AIDL DeviceState 49 */ 50 51 /** 52 * The device is in its normal physical configuration. This is the default if the 53 * device does not support multiple different states. 54 */ 55 public static final long NORMAL = 0; 56 57 /** 58 * The device is folded. If not set, the device is unfolded or does not 59 * support folding. 60 * 61 * The exact point when this status change happens during the folding 62 * operation is device-specific. 63 */ 64 public static final long FOLDED = 1 << 2; 65 66 /** @hide */ 67 @Retention(RetentionPolicy.SOURCE) 68 @LongDef(prefix = {"DEVICE_STATE"}, value = 69 {NORMAL, 70 FOLDED }) 71 public @interface DeviceState {}; 72 73 private final HashMap<Long, Integer> mDeviceStateOrientationMap; 74 75 /** 76 * Create a new immutable DeviceStateOrientationMap instance. 77 * 78 * <p>The array is a list of pairs of elements (deviceState, angle):</p> 79 * 80 * <code>[state0, angle0, state1, angle1,..., stateN, angleN]</code> 81 * 82 * <p>Each pair describes the camera sensor orientation when the device is in the 83 * matching deviceState. The angle is in degrees, and must be a multiple of 90.</p> 84 * 85 * <p>This constructor takes over the array; do not write to the array afterwards.</p> 86 * 87 * @param elements 88 * An array of elements describing the map 89 * 90 * @throws IllegalArgumentException 91 * if the {@code elements} array length is invalid, not divisible by 2 or contains 92 * invalid element values 93 * @throws NullPointerException 94 * if {@code elements} is {@code null} 95 * 96 * @hide 97 */ DeviceStateSensorOrientationMap(@onNull final long[] elements)98 public DeviceStateSensorOrientationMap(@NonNull final long[] elements) { 99 mElements = Objects.requireNonNull(elements, "elements must not be null"); 100 mDeviceStateOrientationMap = new HashMap<>(); 101 if ((elements.length % 2) != 0) { 102 throw new IllegalArgumentException("Device state sensor orientation map length " + 103 elements.length + " is not even!"); 104 } 105 106 for (int i = 0; i < elements.length; i += 2) { 107 if ((elements[i+1] % 90) != 0) { 108 throw new IllegalArgumentException("Sensor orientation not divisible by 90: " + 109 elements[i+1]); 110 } 111 112 mDeviceStateOrientationMap.put(elements[i], Math.toIntExact(elements[i + 1])); 113 } 114 } 115 116 /** 117 * Used by the Builder only. 118 * 119 * @hide 120 */ DeviceStateSensorOrientationMap(@onNull final ArrayList<Long> elements, @NonNull final HashMap<Long, Integer> deviceStateOrientationMap)121 private DeviceStateSensorOrientationMap(@NonNull final ArrayList<Long> elements, 122 @NonNull final HashMap<Long, Integer> deviceStateOrientationMap) { 123 mElements = new long[elements.size()]; 124 for (int i = 0; i < elements.size(); i++) { 125 mElements[i] = elements.get(i); 126 } 127 mDeviceStateOrientationMap = deviceStateOrientationMap; 128 } 129 130 /** 131 * Return the logical camera sensor orientation given a specific device fold state. 132 * 133 * @param deviceState Device fold state 134 * 135 * @return Valid {@link android.hardware.camera2.CameraCharacteristics#SENSOR_ORIENTATION} for 136 * any supported device fold state 137 * 138 * @throws IllegalArgumentException if the given device state is invalid 139 */ getSensorOrientation(@eviceState long deviceState)140 public int getSensorOrientation(@DeviceState long deviceState) { 141 if (!mDeviceStateOrientationMap.containsKey(deviceState)) { 142 throw new IllegalArgumentException("Invalid device state: " + deviceState); 143 } 144 145 return mDeviceStateOrientationMap.get(deviceState); 146 } 147 148 /** 149 * Check if this DeviceStateSensorOrientationMap is equal to another 150 * DeviceStateSensorOrientationMap. 151 * 152 * <p>Two device state orientation maps are equal if and only if all of their elements are 153 * {@link Object#equals equal}.</p> 154 * 155 * @return {@code true} if the objects were equal, {@code false} otherwise 156 */ 157 @Override equals(final Object obj)158 public boolean equals(final Object obj) { 159 if (obj == null) { 160 return false; 161 } 162 if (this == obj) { 163 return true; 164 } 165 if (obj instanceof DeviceStateSensorOrientationMap) { 166 final DeviceStateSensorOrientationMap other = (DeviceStateSensorOrientationMap) obj; 167 return Arrays.equals(mElements, other.mElements); 168 } 169 return false; 170 } 171 172 /** 173 * {@inheritDoc} 174 */ 175 @Override hashCode()176 public int hashCode() { 177 return HashCodeHelpers.hashCodeGeneric(mElements); 178 } 179 180 private final long[] mElements; 181 182 /** 183 * Builds a DeviceStateSensorOrientationMap object. 184 * 185 * <p>This builder is public to allow for easier application testing by 186 * creating custom object instances. It's not necessary to construct these 187 * objects during normal use of the camera API.</p> 188 */ 189 public static final class Builder { Builder()190 public Builder() { 191 // Empty 192 } 193 194 /** 195 * Add a sensor orientation for a given device state. 196 * 197 * <p>Each pair of deviceState and angle describes the camera sensor orientation when the 198 * device is in the matching deviceState. The angle is in degrees, and must be a multiple 199 * of 90.</p> 200 * 201 * @param deviceState The deviceState. 202 * @param angle The orientation angle in degrees. 203 * @return This builder. 204 * 205 */ 206 @SuppressLint("MissingGetterMatchingBuilder") addOrientationForState(@eviceState long deviceState, long angle)207 public @NonNull Builder addOrientationForState(@DeviceState long deviceState, long angle) { 208 if (angle % 90 != 0) { 209 throw new IllegalArgumentException("Sensor orientation not divisible by 90: " 210 + angle); 211 } 212 mDeviceStateOrientationMap.put(deviceState, Math.toIntExact(angle)); 213 mElements.add(deviceState); 214 mElements.add(angle); 215 return this; 216 } 217 218 /** 219 * Returns an instance of <code>DeviceStateSensorOrientationMap</code> created from the 220 * fields set on this builder. 221 * 222 * @return A DeviceStateSensorOrientationMap. 223 */ build()224 public @NonNull DeviceStateSensorOrientationMap build() { 225 if (mElements.size() == 0) { 226 throw new IllegalStateException("Cannot build a DeviceStateSensorOrientationMap" 227 + " with zero elements."); 228 } 229 return new DeviceStateSensorOrientationMap(mElements, mDeviceStateOrientationMap); 230 } 231 232 private final ArrayList<Long> mElements = new ArrayList<>(); 233 private final HashMap<Long, Integer> mDeviceStateOrientationMap = new HashMap<>(); 234 } 235 } 236