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