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