1 /* 2 * Copyright 2014 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.cts.helpers; 18 19 import static org.junit.Assert.assertNotNull; 20 21 import android.content.Context; 22 import android.content.res.Resources; 23 import android.graphics.SurfaceTexture; 24 import android.hardware.Camera; 25 import android.hardware.Camera.Parameters; 26 import android.hardware.Camera.Size; 27 import android.hardware.camera2.CameraCharacteristics; 28 import android.hardware.camera2.CameraManager; 29 import android.hardware.camera2.CameraMetadata; 30 import android.hardware.camera2.cts.helpers.StaticMetadata; 31 import android.hardware.devicestate.DeviceStateManager; 32 import android.os.Bundle; 33 import android.os.SystemClock; 34 import android.util.Log; 35 import android.view.TextureView; 36 37 import androidx.test.InstrumentationRegistry; 38 39 import java.util.Arrays; 40 import java.util.Comparator; 41 import java.util.List; 42 import java.util.Set; 43 import java.util.stream.Collectors; 44 import java.util.stream.IntStream; 45 46 /** 47 * Utility class containing helper functions for the Camera CTS tests. 48 */ 49 public class CameraUtils { 50 private static final float FOCAL_LENGTH_TOLERANCE = .01f; 51 52 53 private static final String TAG = "CameraUtils"; 54 private static final long SHORT_SLEEP_WAIT_TIME_MS = 100; 55 56 /** 57 * Returns {@code true} if this device only supports {@code LEGACY} mode operation in the 58 * Camera2 API for the given camera ID. 59 * 60 * @param context {@link Context} to access the {@link CameraManager} in. 61 * @param cameraId the ID of the camera device to check. 62 * @return {@code true} if this device only supports {@code LEGACY} mode. 63 */ isLegacyHAL(Context context, int cameraId)64 public static boolean isLegacyHAL(Context context, int cameraId) throws Exception { 65 CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 66 String cameraIdStr = manager.getCameraIdListNoLazy()[cameraId]; 67 return isLegacyHAL(manager, cameraIdStr); 68 } 69 70 /** 71 * Returns {@code true} if this device only supports {@code LEGACY} mode operation in the 72 * Camera2 API for the given camera ID. 73 * 74 * @param manager The {@link CameraManager} used to retrieve camera characteristics. 75 * @param cameraId the ID of the camera device to check. 76 * @return {@code true} if this device only supports {@code LEGACY} mode. 77 */ isLegacyHAL(CameraManager manager, String cameraIdStr)78 public static boolean isLegacyHAL(CameraManager manager, String cameraIdStr) throws Exception { 79 CameraCharacteristics characteristics = 80 manager.getCameraCharacteristics(cameraIdStr); 81 82 return characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == 83 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; 84 } 85 86 /** 87 * Returns {@code true} if the Camera.Parameter and Camera.Info arguments describe a similar 88 * camera as the CameraCharacteristics. 89 * 90 * @param params Camera.Parameters to use for matching. 91 * @param info Camera.CameraInfo to use for matching. 92 * @param ch CameraCharacteristics to use for matching. 93 * @return {@code true} if the arguments describe similar camera devices. 94 */ matchParametersToCharacteristics(Camera.Parameters params, Camera.CameraInfo info, CameraCharacteristics ch)95 public static boolean matchParametersToCharacteristics(Camera.Parameters params, 96 Camera.CameraInfo info, CameraCharacteristics ch) { 97 Integer facing = ch.get(CameraCharacteristics.LENS_FACING); 98 switch (facing.intValue()) { 99 case CameraMetadata.LENS_FACING_EXTERNAL: 100 if (info.facing != Camera.CameraInfo.CAMERA_FACING_FRONT && 101 info.facing != Camera.CameraInfo.CAMERA_FACING_BACK) { 102 return false; 103 } 104 break; 105 case CameraMetadata.LENS_FACING_FRONT: 106 if (info.facing != Camera.CameraInfo.CAMERA_FACING_FRONT) { 107 return false; 108 } 109 break; 110 case CameraMetadata.LENS_FACING_BACK: 111 if (info.facing != Camera.CameraInfo.CAMERA_FACING_BACK) { 112 return false; 113 } 114 break; 115 default: 116 return false; 117 } 118 119 Integer orientation = ch.get(CameraCharacteristics.SENSOR_ORIENTATION); 120 if (orientation.intValue() != info.orientation) { 121 return false; 122 } 123 124 StaticMetadata staticMeta = new StaticMetadata(ch); 125 boolean legacyHasFlash = params.getSupportedFlashModes() != null; 126 if (staticMeta.hasFlash() != legacyHasFlash) { 127 return false; 128 } 129 130 boolean isExternal = (ch.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == 131 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL); 132 boolean hasValidMinFocusDistance = staticMeta.areKeysAvailable( 133 CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); 134 boolean fixedFocusExternal = isExternal && !hasValidMinFocusDistance; 135 boolean hasFocuser = staticMeta.hasFocuser() && !fixedFocusExternal; 136 List<String> legacyFocusModes = params.getSupportedFocusModes(); 137 boolean legacyHasFocuser = !((legacyFocusModes.size() == 1) && 138 (legacyFocusModes.contains(Camera.Parameters.FOCUS_MODE_FIXED))); 139 if (hasFocuser != legacyHasFocuser) { 140 return false; 141 } 142 143 if (staticMeta.isVideoStabilizationSupported() != 144 params.isVideoStabilizationSupported()) { 145 return false; 146 } 147 148 float legacyFocalLength = params.getFocalLength(); 149 if (ch.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) != null) { 150 float [] focalLengths = staticMeta.getAvailableFocalLengthsChecked(); 151 boolean found = false; 152 for (float focalLength : focalLengths) { 153 if (Math.abs(focalLength - legacyFocalLength) <= FOCAL_LENGTH_TOLERANCE) { 154 found = true; 155 break; 156 } 157 } 158 return found; 159 } else if (legacyFocalLength != -1.0f) { 160 return false; 161 } 162 163 return true; 164 } 165 166 /** 167 * Returns {@code true} if this device only supports {@code EXTERNAL} mode operation in the 168 * Camera2 API for the given camera ID. 169 * 170 * @param context {@link Context} to access the {@link CameraManager} in. 171 * @param cameraId the ID of the camera device to check. 172 * @return {@code true} if this device only supports {@code LEGACY} mode. 173 */ isExternal(Context context, int cameraId)174 public static boolean isExternal(Context context, int cameraId) throws Exception { 175 CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); 176 177 Camera camera = null; 178 Camera.Parameters params = null; 179 Camera.CameraInfo info = new Camera.CameraInfo(); 180 try { 181 Camera.getCameraInfo(cameraId, info); 182 camera = Camera.open(cameraId); 183 params = camera.getParameters(); 184 } finally { 185 if (camera != null) { 186 camera.release(); 187 } 188 } 189 190 String [] cameraIdList = manager.getCameraIdList(); 191 CameraCharacteristics characteristics = 192 manager.getCameraCharacteristics(cameraIdList[cameraId]); 193 194 if (!matchParametersToCharacteristics(params, info, characteristics)) { 195 boolean found = false; 196 for (String id : cameraIdList) { 197 characteristics = manager.getCameraCharacteristics(id); 198 if (matchParametersToCharacteristics(params, info, characteristics)) { 199 found = true; 200 break; 201 } 202 } 203 if (!found) { 204 return false; 205 } 206 } 207 208 return characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) == 209 CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL; 210 } 211 212 /** 213 * Shared size comparison method used by size comparators. 214 * 215 * <p>Compares the number of pixels it covers.If two the areas of two sizes are same, compare 216 * the widths.</p> 217 */ compareSizes(int widthA, int heightA, int widthB, int heightB)218 public static int compareSizes(int widthA, int heightA, int widthB, int heightB) { 219 long left = widthA * (long) heightA; 220 long right = widthB * (long) heightB; 221 if (left == right) { 222 left = widthA; 223 right = widthB; 224 } 225 return (left < right) ? -1 : (left > right ? 1 : 0); 226 } 227 228 /** 229 * Size comparator that compares the number of pixels it covers. 230 * 231 * <p>If two the areas of two sizes are same, compare the widths.</p> 232 */ 233 public static class LegacySizeComparator implements Comparator<Camera.Size> { 234 @Override compare(Camera.Size lhs, Camera.Size rhs)235 public int compare(Camera.Size lhs, Camera.Size rhs) { 236 return compareSizes(lhs.width, lhs.height, rhs.width, rhs.height); 237 } 238 } 239 getOverrideCameraId()240 public static String getOverrideCameraId() { 241 Bundle bundle = InstrumentationRegistry.getArguments(); 242 return bundle.getString("camera-id"); 243 } 244 deriveCameraIdsUnderTest()245 public static int[] deriveCameraIdsUnderTest() throws Exception { 246 String overrideId = getOverrideCameraId(); 247 int numberOfCameras = Camera.getNumberOfCameras(); 248 int[] cameraIds; 249 if (overrideId == null) { 250 cameraIds = IntStream.range(0, numberOfCameras).toArray(); 251 } else { 252 int overrideCameraId = Integer.parseInt(overrideId); 253 if (overrideCameraId >= 0 && overrideCameraId < numberOfCameras) { 254 cameraIds = new int[]{overrideCameraId}; 255 } else { 256 cameraIds = new int[]{}; 257 } 258 } 259 return cameraIds; 260 } 261 262 /** 263 * Wait until the SurfaceTexture available from the TextureView, then return it. 264 * Return null if the wait times out. 265 * 266 * @param timeOutMs The timeout value for the wait 267 * @return The available SurfaceTexture, return null if the wait times out. 268 */ getAvailableSurfaceTexture(long timeOutMs, TextureView view)269 public static SurfaceTexture getAvailableSurfaceTexture(long timeOutMs, TextureView view) { 270 long waitTime = timeOutMs; 271 272 while (!view.isAvailable() && waitTime > 0) { 273 long startTimeMs = SystemClock.elapsedRealtime(); 274 SystemClock.sleep(SHORT_SLEEP_WAIT_TIME_MS); 275 waitTime -= (SystemClock.elapsedRealtime() - startTimeMs); 276 } 277 278 if (view.isAvailable()) { 279 return view.getSurfaceTexture(); 280 } else { 281 Log.w(TAG, "Wait for SurfaceTexture available timed out after " + timeOutMs + "ms"); 282 return null; 283 } 284 } 285 286 /** 287 * Uses {@link DeviceStateManager} to determine if the device is foldable or not. It relies on 288 * the OEM exposing supported states, and setting 289 * com.android.internal.R.array.config_foldedDeviceStates correctly with the folded states. 290 * 291 * @return true is the device is a foldable; false otherwise 292 */ isDeviceFoldable(Context mContext)293 public static boolean isDeviceFoldable(Context mContext) { 294 DeviceStateManager deviceStateManager = 295 mContext.getSystemService(DeviceStateManager.class); 296 if (deviceStateManager == null) { 297 Log.w(TAG, "Couldn't locate DeviceStateManager to detect if the device is foldable" 298 + " or not. Defaulting to not-foldable."); 299 return false; 300 } 301 Set<Integer> supportedStates = Arrays.stream( 302 deviceStateManager.getSupportedStates()).boxed().collect(Collectors.toSet()); 303 304 Resources systemRes = Resources.getSystem(); 305 int foldedStatesArrayIdentifier = systemRes.getIdentifier("config_foldedDeviceStates", 306 "array", "android"); 307 int[] foldedDeviceStates = systemRes.getIntArray(foldedStatesArrayIdentifier); 308 309 // Device is a foldable if supportedStates contains any state in foldedDeviceStates 310 return Arrays.stream(foldedDeviceStates).anyMatch(supportedStates::contains); 311 } 312 313 /** 314 * Returns Gets the supported video frame sizes that can be used by MediaRecorder. 315 * 316 * @param camera Camera for which to get the supported video sizes. 317 * @return a list of Size object if camera has separate preview and video output; 318 * in case there isn't a separate video/preview size option, 319 * it returns the preview sizes. 320 */ getSupportedVideoSizes(Camera camera)321 public static List<Size> getSupportedVideoSizes(Camera camera) { 322 Parameters parameters = camera.getParameters(); 323 assertNotNull("Camera did not provide parameters", parameters); 324 List<Size> videoSizes = parameters.getSupportedVideoSizes(); 325 326 if (videoSizes == null) { 327 videoSizes = parameters.getSupportedPreviewSizes(); 328 assertNotNull(videoSizes); 329 } 330 331 return videoSizes; 332 } 333 } 334