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