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.camera2.cts;
18 
19 import static android.hardware.camera2.cts.CameraTestUtils.*;
20 import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.*;
21 
22 import android.content.Context;
23 import android.graphics.ImageFormat;
24 import android.graphics.SurfaceTexture;
25 import android.hardware.camera2.CameraCaptureSession;
26 import android.hardware.camera2.CameraCharacteristics;
27 import android.hardware.camera2.CameraDevice;
28 import android.hardware.camera2.CameraManager;
29 import android.hardware.camera2.CaptureRequest;
30 import android.hardware.camera2.CaptureResult;
31 import android.hardware.camera2.TotalCaptureResult;
32 import android.hardware.camera2.CaptureFailure;
33 import android.hardware.camera2.params.InputConfiguration;
34 import android.hardware.camera2.params.StreamConfigurationMap;
35 import android.hardware.camera2.cts.helpers.StaticMetadata;
36 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
37 import android.media.CamcorderProfile;
38 import android.media.Image;
39 import android.media.ImageReader;
40 import android.media.ImageWriter;
41 import android.util.Log;
42 import android.util.Size;
43 import android.view.Display;
44 import android.view.Surface;
45 import android.view.WindowManager;
46 
47 import com.android.ex.camera2.blocking.BlockingSessionCallback;
48 
49 import java.util.Arrays;
50 import java.util.ArrayList;
51 import java.util.List;
52 
53 import static junit.framework.Assert.assertTrue;
54 import static org.mockito.Mockito.*;
55 
56 /**
57  * Tests exercising edge cases in camera setup, configuration, and usage.
58  */
59 public class RobustnessTest extends Camera2AndroidTestCase {
60     private static final String TAG = "RobustnessTest";
61     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
62 
63     private static final int CONFIGURE_TIMEOUT = 5000; //ms
64     private static final int CAPTURE_TIMEOUT = 1000; //ms
65 
66     // For testTriggerInteractions
67     private static final int PREVIEW_WARMUP_FRAMES = 60;
68     private static final int MAX_RESULT_STATE_CHANGE_WAIT_FRAMES = 100;
69     private static final int MAX_TRIGGER_SEQUENCE_FRAMES = 180; // 6 sec at 30 fps
70     private static final int MAX_RESULT_STATE_POSTCHANGE_WAIT_FRAMES = 10;
71 
72     /**
73      * Test that a {@link CameraCaptureSession} can be configured with a {@link Surface} containing
74      * a dimension other than one of the supported output dimensions.  The buffers produced into
75      * this surface are expected have the dimensions of the closest possible buffer size in the
76      * available stream configurations for a surface with this format.
77      */
testBadSurfaceDimensions()78     public void testBadSurfaceDimensions() throws Exception {
79         for (String id : mCameraIds) {
80             try {
81                 Log.i(TAG, "Testing Camera " + id);
82                 openDevice(id);
83 
84                 List<Size> testSizes = null;
85                 int format = mStaticInfo.isColorOutputSupported() ?
86                     ImageFormat.YUV_420_888 : ImageFormat.DEPTH16;
87 
88                 testSizes = CameraTestUtils.getSortedSizesForFormat(id, mCameraManager,
89                         format, null);
90 
91                 // Find some size not supported by the camera
92                 Size weirdSize = new Size(643, 577);
93                 int count = 0;
94                 while(testSizes.contains(weirdSize)) {
95                     // Really, they can't all be supported...
96                     weirdSize = new Size(weirdSize.getWidth() + 1, weirdSize.getHeight() + 1);
97                     count++;
98                     assertTrue("Too many exotic YUV_420_888 resolutions supported.", count < 100);
99                 }
100 
101                 // Setup imageReader with invalid dimension
102                 ImageReader imageReader = ImageReader.newInstance(weirdSize.getWidth(),
103                         weirdSize.getHeight(), format, 3);
104 
105                 // Setup ImageReaderListener
106                 SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
107                 imageReader.setOnImageAvailableListener(imageListener, mHandler);
108 
109                 Surface surface = imageReader.getSurface();
110                 List<Surface> surfaces = new ArrayList<>();
111                 surfaces.add(surface);
112 
113                 // Setup a capture request and listener
114                 CaptureRequest.Builder request =
115                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
116                 request.addTarget(surface);
117 
118                 // Check that correct session callback is hit.
119                 CameraCaptureSession.StateCallback sessionListener =
120                         mock(CameraCaptureSession.StateCallback.class);
121                 CameraCaptureSession session = CameraTestUtils.configureCameraSession(mCamera,
122                         surfaces, sessionListener, mHandler);
123 
124                 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
125                         onConfigured(any(CameraCaptureSession.class));
126                 verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
127                         onReady(any(CameraCaptureSession.class));
128                 verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class));
129                 verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
130                 verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
131 
132                 CameraCaptureSession.CaptureCallback captureListener =
133                         mock(CameraCaptureSession.CaptureCallback.class);
134                 session.capture(request.build(), captureListener, mHandler);
135 
136                 verify(captureListener, timeout(CAPTURE_TIMEOUT).atLeastOnce()).
137                         onCaptureCompleted(any(CameraCaptureSession.class),
138                                 any(CaptureRequest.class), any(TotalCaptureResult.class));
139                 verify(captureListener, never()).onCaptureFailed(any(CameraCaptureSession.class),
140                         any(CaptureRequest.class), any(CaptureFailure.class));
141 
142                 Image image = imageListener.getImage(CAPTURE_TIMEOUT);
143                 int imageWidth = image.getWidth();
144                 int imageHeight = image.getHeight();
145                 Size actualSize = new Size(imageWidth, imageHeight);
146 
147                 assertTrue("Camera does not contain outputted image resolution " + actualSize,
148                         testSizes.contains(actualSize));
149             } finally {
150                 closeDevice(id);
151             }
152         }
153     }
154 
155     /**
156      * Test for making sure the required output combinations for each hardware level and capability
157      * work as expected.
158      */
testMandatoryOutputCombinations()159     public void testMandatoryOutputCombinations() throws Exception {
160         /**
161          * Tables for maximum sizes to try for each hardware level and capability.
162          *
163          * Keep in sync with the tables in
164          * frameworks/base/core/java/android/hardware/camera2/CameraDevice.java#createCaptureSession
165          *
166          * Each row of the table is a set of (format, max resolution) pairs, using the below consts
167          */
168 
169         // Enum values are defined in MaxStreamSizes
170         final int[][] LEGACY_COMBINATIONS = {
171             {PRIV, MAXIMUM}, // Simple preview, GPU video processing, or no-preview video recording
172             {JPEG, MAXIMUM}, // No-viewfinder still image capture
173             {YUV,  MAXIMUM}, // In-application video/image processing
174             {PRIV, PREVIEW,  JPEG, MAXIMUM}, // Standard still imaging.
175             {YUV,  PREVIEW,  JPEG, MAXIMUM}, // In-app processing plus still capture.
176             {PRIV, PREVIEW,  PRIV, PREVIEW}, // Standard recording.
177             {PRIV, PREVIEW,  YUV,  PREVIEW}, // Preview plus in-app processing.
178             {PRIV, PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM} // Still capture plus in-app processing.
179         };
180 
181         final int[][] LIMITED_COMBINATIONS = {
182             {PRIV, PREVIEW,  PRIV, RECORD }, // High-resolution video recording with preview.
183             {PRIV, PREVIEW,  YUV , RECORD }, // High-resolution in-app video processing with preview.
184             {YUV , PREVIEW,  YUV , RECORD }, // Two-input in-app video processing.
185             {PRIV, PREVIEW,  PRIV, RECORD,   JPEG, RECORD  }, // High-resolution recording with video snapshot.
186             {PRIV, PREVIEW,  YUV,  RECORD,   JPEG, RECORD  }, // High-resolution in-app processing with video snapshot.
187             {YUV , PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM }  // Two-input in-app processing with still capture.
188         };
189 
190         final int[][] BURST_COMBINATIONS = {
191             {PRIV, PREVIEW,  PRIV, MAXIMUM }, // Maximum-resolution GPU processing with preview.
192             {PRIV, PREVIEW,  YUV,  MAXIMUM }, // Maximum-resolution in-app processing with preview.
193             {YUV,  PREVIEW,  YUV,  MAXIMUM }, // Maximum-resolution two-input in-app processsing.
194         };
195 
196         final int[][] FULL_COMBINATIONS = {
197             {PRIV, PREVIEW,  PRIV, PREVIEW,  JPEG, MAXIMUM }, //Video recording with maximum-size video snapshot.
198             {YUV,  VGA,      PRIV, PREVIEW,  YUV,  MAXIMUM }, // Standard video recording plus maximum-resolution in-app processing.
199             {YUV,  VGA,      YUV,  PREVIEW,  YUV,  MAXIMUM } // Preview plus two-input maximum-resolution in-app processing.
200         };
201 
202         final int[][] RAW_COMBINATIONS = {
203             {RAW,  MAXIMUM }, // No-preview DNG capture.
204             {PRIV, PREVIEW,  RAW,  MAXIMUM }, // Standard DNG capture.
205             {YUV,  PREVIEW,  RAW,  MAXIMUM }, // In-app processing plus DNG capture.
206             {PRIV, PREVIEW,  PRIV, PREVIEW,  RAW, MAXIMUM}, // Video recording with DNG capture.
207             {PRIV, PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM}, // Preview with in-app processing and DNG capture.
208             {YUV,  PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM}, // Two-input in-app processing plus DNG capture.
209             {PRIV, PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM}, // Still capture with simultaneous JPEG and DNG.
210             {YUV,  PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM}  // In-app processing with simultaneous JPEG and DNG.
211         };
212 
213         final int[][][] TABLES =
214             { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
215 
216         sanityCheckConfigurationTables(TABLES);
217 
218         for (String id : mCameraIds) {
219             openDevice(id);
220 
221             // Find the concrete max sizes for each format/resolution combination
222             MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id, getContext());
223 
224             String streamConfigurationMapString =
225                     mStaticInfo.getCharacteristics().get(
226                             CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).toString();
227             if (VERBOSE) {
228                 Log.v(TAG, "StreamConfigurationMap: " + streamConfigurationMapString);
229             }
230 
231             // Always run legacy-level tests for color-supporting devices
232 
233             if (mStaticInfo.isColorOutputSupported()) {
234                 for (int[] config : LEGACY_COMBINATIONS) {
235                     testOutputCombination(id, config, maxSizes);
236                 }
237             }
238 
239             // Then run higher-level tests if applicable
240 
241             if (!mStaticInfo.isHardwareLevelLegacy()) {
242 
243                 // If not legacy, at least limited, so run limited-level tests
244 
245                 if (mStaticInfo.isColorOutputSupported()) {
246                     for (int[] config : LIMITED_COMBINATIONS) {
247                         testOutputCombination(id, config, maxSizes);
248                     }
249                 }
250 
251                 // Check for BURST_CAPTURE, FULL and RAW and run those if appropriate
252 
253                 if (mStaticInfo.isCapabilitySupported(
254                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
255                     for (int[] config : BURST_COMBINATIONS) {
256                         testOutputCombination(id, config, maxSizes);
257                     }
258                 }
259 
260                 if (mStaticInfo.isHardwareLevelFull()) {
261                     for (int[] config : FULL_COMBINATIONS) {
262                         testOutputCombination(id, config, maxSizes);
263                     }
264                 }
265 
266                 if (mStaticInfo.isCapabilitySupported(
267                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
268                     for (int[] config : RAW_COMBINATIONS) {
269                         testOutputCombination(id, config, maxSizes);
270                     }
271                 }
272             }
273 
274             closeDevice(id);
275         }
276     }
277 
278     /**
279      * Test for making sure the required reprocess input/output combinations for each hardware
280      * level and capability work as expected.
281      */
testMandatoryReprocessConfigurations()282     public void testMandatoryReprocessConfigurations() throws Exception {
283 
284         /**
285          * For each stream combination, verify that
286          *    1. A reprocessable session can be created using the stream combination.
287          *    2. Reprocess capture requests targeting YUV and JPEG outputs are successful.
288          */
289         final int[][] LIMITED_COMBINATIONS = {
290             // Input        Outputs
291             {PRIV, MAXIMUM, JPEG, MAXIMUM},
292             {YUV , MAXIMUM, JPEG, MAXIMUM},
293             {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM},
294             {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM},
295             {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM},
296             {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM},
297             {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
298             {YUV,  MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
299         };
300 
301         final int[][] FULL_COMBINATIONS = {
302             // Input        Outputs
303             {YUV , MAXIMUM, PRIV, PREVIEW},
304             {YUV , MAXIMUM, YUV , PREVIEW},
305             {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , RECORD},
306             {YUV , MAXIMUM, PRIV, PREVIEW, YUV , RECORD},
307             {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , MAXIMUM},
308             {PRIV, MAXIMUM, YUV , PREVIEW, YUV , MAXIMUM},
309             {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
310             {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
311         };
312 
313         final int[][] RAW_COMBINATIONS = {
314             // Input        Outputs
315             {PRIV, MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM},
316             {YUV , MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM},
317             {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
318             {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
319             {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
320             {YUV , MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
321             {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
322             {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
323             {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
324             {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
325         };
326 
327         final int[][][] TABLES =
328                 { LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
329 
330         sanityCheckConfigurationTables(TABLES);
331 
332         for (String id : mCameraIds) {
333             CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
334             StaticMetadata staticInfo = new StaticMetadata(cc);
335             MaxStreamSizes maxSizes = new MaxStreamSizes(staticInfo, id, getContext());
336 
337             // Skip the test for legacy devices.
338             if (staticInfo.isHardwareLevelLegacy()) {
339                 continue;
340             }
341 
342             openDevice(id);
343 
344             try {
345                 for (int[] config : LIMITED_COMBINATIONS) {
346                     testReprocessStreamCombination(id, config, maxSizes, staticInfo);
347                 }
348 
349                 // Check FULL devices
350                 if (staticInfo.isHardwareLevelFull()) {
351                     for (int[] config : FULL_COMBINATIONS) {
352                         testReprocessStreamCombination(id, config, maxSizes, staticInfo);
353                     }
354                 }
355 
356                 // Check devices with RAW capability.
357                 if (staticInfo.isCapabilitySupported(
358                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
359                     for (int[] config : RAW_COMBINATIONS) {
360                         testReprocessStreamCombination(id, config, maxSizes, staticInfo);
361                     }
362                 }
363             } finally {
364                 closeDevice(id);
365             }
366         }
367     }
368 
testBasicTriggerSequence()369     public void testBasicTriggerSequence() throws Exception {
370 
371         for (String id : mCameraIds) {
372             Log.i(TAG, String.format("Testing Camera %s", id));
373 
374             openDevice(id);
375             try {
376                 // Legacy devices do not support precapture trigger; don't test devices that
377                 // can't focus
378                 if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) {
379                     continue;
380                 }
381                 // Depth-only devices won't support AE
382                 if (!mStaticInfo.isColorOutputSupported()) {
383                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
384                     continue;
385                 }
386 
387                 int[] availableAfModes = mStaticInfo.getCharacteristics().get(
388                     CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
389                 int[] availableAeModes = mStaticInfo.getCharacteristics().get(
390                     CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
391 
392                 for (int afMode : availableAfModes) {
393 
394                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
395                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
396                         // Only test AF modes that have meaningful trigger behavior
397                         continue;
398                     }
399 
400                     for (int aeMode : availableAeModes) {
401                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
402                             // Only test AE modes that have meaningful trigger behavior
403                             continue;
404                         }
405 
406                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
407 
408                         CaptureRequest.Builder previewRequest =
409                                 prepareTriggerTestSession(preview, aeMode, afMode);
410 
411                         SimpleCaptureCallback captureListener =
412                                 new CameraTestUtils.SimpleCaptureCallback();
413 
414                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
415                                 mHandler);
416 
417                         // Cancel triggers
418 
419                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
420 
421                         //
422                         // Standard sequence - AF trigger then AE trigger
423 
424                         if (VERBOSE) {
425                             Log.v(TAG, String.format("Triggering AF"));
426                         }
427 
428                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
429                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
430                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
431                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
432 
433                         CaptureRequest triggerRequest = previewRequest.build();
434                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
435 
436                         CaptureResult triggerResult = captureListener.getCaptureResultForRequest(
437                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
438                         int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
439                         boolean focusComplete = false;
440 
441                         for (int i = 0;
442                              i < MAX_TRIGGER_SEQUENCE_FRAMES && !focusComplete;
443                              i++) {
444 
445                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
446 
447                             CaptureResult focusResult = captureListener.getCaptureResult(
448                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
449                             afState = focusResult.get(CaptureResult.CONTROL_AF_STATE);
450                         }
451 
452                         assertTrue("Focusing never completed!", focusComplete);
453 
454                         // Standard sequence - Part 2 AE trigger
455 
456                         if (VERBOSE) {
457                             Log.v(TAG, String.format("Triggering AE"));
458                         }
459 
460                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
461                                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
462                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
463                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
464 
465                         triggerRequest = previewRequest.build();
466                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
467 
468                         triggerResult = captureListener.getCaptureResultForRequest(
469                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
470 
471                         int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
472 
473                         boolean precaptureComplete = false;
474 
475                         for (int i = 0;
476                              i < MAX_TRIGGER_SEQUENCE_FRAMES && !precaptureComplete;
477                              i++) {
478 
479                             precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
480 
481                             CaptureResult precaptureResult = captureListener.getCaptureResult(
482                                 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
483                             aeState = precaptureResult.get(CaptureResult.CONTROL_AE_STATE);
484                         }
485 
486                         assertTrue("Precapture sequence never completed!", precaptureComplete);
487 
488                         for (int i = 0; i < MAX_RESULT_STATE_POSTCHANGE_WAIT_FRAMES; i++) {
489                             CaptureResult postPrecaptureResult = captureListener.getCaptureResult(
490                                 CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
491                             aeState = postPrecaptureResult.get(CaptureResult.CONTROL_AE_STATE);
492                             assertTrue("Late transition to PRECAPTURE state seen",
493                                     aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE);
494                         }
495 
496                         // Done
497 
498                         stopCapture(/*fast*/ false);
499                         preview.release();
500                     }
501 
502                 }
503 
504             } finally {
505                 closeDevice(id);
506             }
507         }
508 
509     }
510 
testSimultaneousTriggers()511     public void testSimultaneousTriggers() throws Exception {
512         for (String id : mCameraIds) {
513             Log.i(TAG, String.format("Testing Camera %s", id));
514 
515             openDevice(id);
516             try {
517                 // Legacy devices do not support precapture trigger; don't test devices that
518                 // can't focus
519                 if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) {
520                     continue;
521                 }
522                 // Depth-only devices won't support AE
523                 if (!mStaticInfo.isColorOutputSupported()) {
524                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
525                     continue;
526                 }
527 
528                 int[] availableAfModes = mStaticInfo.getCharacteristics().get(
529                     CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
530                 int[] availableAeModes = mStaticInfo.getCharacteristics().get(
531                     CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
532 
533                 for (int afMode : availableAfModes) {
534 
535                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
536                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
537                         // Only test AF modes that have meaningful trigger behavior
538                         continue;
539                     }
540 
541                     for (int aeMode : availableAeModes) {
542                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
543                             // Only test AE modes that have meaningful trigger behavior
544                             continue;
545                         }
546 
547                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
548 
549                         CaptureRequest.Builder previewRequest =
550                                 prepareTriggerTestSession(preview, aeMode, afMode);
551 
552                         SimpleCaptureCallback captureListener =
553                                 new CameraTestUtils.SimpleCaptureCallback();
554 
555                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
556                                 mHandler);
557 
558                         // Cancel triggers
559 
560                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
561 
562                         //
563                         // Trigger AF and AE together
564 
565                         if (VERBOSE) {
566                             Log.v(TAG, String.format("Triggering AF and AE together"));
567                         }
568 
569                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
570                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
571                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
572                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
573 
574                         CaptureRequest triggerRequest = previewRequest.build();
575                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
576 
577                         CaptureResult triggerResult = captureListener.getCaptureResultForRequest(
578                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
579                         int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
580                         int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
581 
582                         boolean precaptureComplete = false;
583                         boolean focusComplete = false;
584 
585                         for (int i = 0;
586                              i < MAX_TRIGGER_SEQUENCE_FRAMES &&
587                                      !(focusComplete && precaptureComplete);
588                              i++) {
589 
590                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
591                             precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
592 
593                             CaptureResult sequenceResult = captureListener.getCaptureResult(
594                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
595                             afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE);
596                             aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE);
597                         }
598 
599                         assertTrue("Precapture sequence never completed!", precaptureComplete);
600                         assertTrue("Focus sequence never completed!", focusComplete);
601 
602                         // Done
603 
604                         stopCapture(/*fast*/ false);
605                         preview.release();
606 
607                     }
608                 }
609             } finally {
610                 closeDevice(id);
611             }
612         }
613     }
614 
testAfThenAeTrigger()615     public void testAfThenAeTrigger() throws Exception {
616         for (String id : mCameraIds) {
617             Log.i(TAG, String.format("Testing Camera %s", id));
618 
619             openDevice(id);
620             try {
621                 // Legacy devices do not support precapture trigger; don't test devices that
622                 // can't focus
623                 if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) {
624                     continue;
625                 }
626                 // Depth-only devices won't support AE
627                 if (!mStaticInfo.isColorOutputSupported()) {
628                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
629                     continue;
630                 }
631 
632                 int[] availableAfModes = mStaticInfo.getCharacteristics().get(
633                     CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
634                 int[] availableAeModes = mStaticInfo.getCharacteristics().get(
635                     CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
636 
637                 for (int afMode : availableAfModes) {
638 
639                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
640                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
641                         // Only test AF modes that have meaningful trigger behavior
642                         continue;
643                     }
644 
645                     for (int aeMode : availableAeModes) {
646                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
647                             // Only test AE modes that have meaningful trigger behavior
648                             continue;
649                         }
650 
651                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
652 
653                         CaptureRequest.Builder previewRequest =
654                                 prepareTriggerTestSession(preview, aeMode, afMode);
655 
656                         SimpleCaptureCallback captureListener =
657                                 new CameraTestUtils.SimpleCaptureCallback();
658 
659                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
660                                 mHandler);
661 
662                         // Cancel triggers
663 
664                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
665 
666                         //
667                         // AF with AE a request later
668 
669                         if (VERBOSE) {
670                             Log.v(TAG, "Trigger AF, then AE trigger on next request");
671                         }
672 
673                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
674                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
675                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
676                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
677 
678                         CaptureRequest triggerRequest = previewRequest.build();
679                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
680 
681                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
682                                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
683                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
684                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
685 
686                         CaptureRequest triggerRequest2 = previewRequest.build();
687                         mCameraSession.capture(triggerRequest2, captureListener, mHandler);
688 
689                         CaptureResult triggerResult = captureListener.getCaptureResultForRequest(
690                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
691                         int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
692 
693                         boolean precaptureComplete = false;
694                         boolean focusComplete = false;
695 
696                         focusComplete = verifyAfSequence(afMode, afState, focusComplete);
697 
698                         triggerResult = captureListener.getCaptureResultForRequest(
699                                 triggerRequest2, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
700                         afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
701                         int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
702 
703                         for (int i = 0;
704                              i < MAX_TRIGGER_SEQUENCE_FRAMES &&
705                                      !(focusComplete && precaptureComplete);
706                              i++) {
707 
708                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
709                             precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
710 
711                             CaptureResult sequenceResult = captureListener.getCaptureResult(
712                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
713                             afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE);
714                             aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE);
715                         }
716 
717                         assertTrue("Precapture sequence never completed!", precaptureComplete);
718                         assertTrue("Focus sequence never completed!", focusComplete);
719 
720                         // Done
721 
722                         stopCapture(/*fast*/ false);
723                         preview.release();
724 
725                     }
726                 }
727             } finally {
728                 closeDevice(id);
729             }
730         }
731     }
732 
testAeThenAfTrigger()733     public void testAeThenAfTrigger() throws Exception {
734         for (String id : mCameraIds) {
735             Log.i(TAG, String.format("Testing Camera %s", id));
736 
737             openDevice(id);
738             try {
739                 // Legacy devices do not support precapture trigger; don't test devices that
740                 // can't focus
741                 if (mStaticInfo.isHardwareLevelLegacy() || !mStaticInfo.hasFocuser()) {
742                     continue;
743                 }
744                 // Depth-only devices won't support AE
745                 if (!mStaticInfo.isColorOutputSupported()) {
746                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
747                     continue;
748                 }
749 
750                 int[] availableAfModes = mStaticInfo.getCharacteristics().get(
751                     CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
752                 int[] availableAeModes = mStaticInfo.getCharacteristics().get(
753                     CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
754 
755                 for (int afMode : availableAfModes) {
756 
757                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
758                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
759                         // Only test AF modes that have meaningful trigger behavior
760                         continue;
761                     }
762 
763                     for (int aeMode : availableAeModes) {
764                         if (aeMode ==  CameraCharacteristics.CONTROL_AE_MODE_OFF) {
765                             // Only test AE modes that have meaningful trigger behavior
766                             continue;
767                         }
768 
769                         SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1);
770 
771                         CaptureRequest.Builder previewRequest =
772                                 prepareTriggerTestSession(preview, aeMode, afMode);
773 
774                         SimpleCaptureCallback captureListener =
775                                 new CameraTestUtils.SimpleCaptureCallback();
776 
777                         mCameraSession.setRepeatingRequest(previewRequest.build(), captureListener,
778                                 mHandler);
779 
780                         // Cancel triggers
781 
782                         cancelTriggersAndWait(previewRequest, captureListener, afMode);
783 
784                         //
785                         // AE with AF a request later
786 
787                         if (VERBOSE) {
788                             Log.v(TAG, "Trigger AE, then AF trigger on next request");
789                         }
790 
791                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
792                                 CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
793                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
794                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
795 
796                         CaptureRequest triggerRequest = previewRequest.build();
797                         mCameraSession.capture(triggerRequest, captureListener, mHandler);
798 
799                         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
800                                 CaptureRequest.CONTROL_AF_TRIGGER_START);
801                         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
802                                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
803 
804                         CaptureRequest triggerRequest2 = previewRequest.build();
805                         mCameraSession.capture(triggerRequest2, captureListener, mHandler);
806 
807                         CaptureResult triggerResult = captureListener.getCaptureResultForRequest(
808                                 triggerRequest, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
809                         int aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
810 
811                         boolean precaptureComplete = false;
812                         boolean focusComplete = false;
813 
814                         precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
815 
816                         triggerResult = captureListener.getCaptureResultForRequest(
817                                 triggerRequest2, MAX_RESULT_STATE_CHANGE_WAIT_FRAMES);
818                         int afState = triggerResult.get(CaptureResult.CONTROL_AF_STATE);
819                         aeState = triggerResult.get(CaptureResult.CONTROL_AE_STATE);
820 
821                         for (int i = 0;
822                              i < MAX_TRIGGER_SEQUENCE_FRAMES &&
823                                      !(focusComplete && precaptureComplete);
824                              i++) {
825 
826                             focusComplete = verifyAfSequence(afMode, afState, focusComplete);
827                             precaptureComplete = verifyAeSequence(aeState, precaptureComplete);
828 
829                             CaptureResult sequenceResult = captureListener.getCaptureResult(
830                                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
831                             afState = sequenceResult.get(CaptureResult.CONTROL_AF_STATE);
832                             aeState = sequenceResult.get(CaptureResult.CONTROL_AE_STATE);
833                         }
834 
835                         assertTrue("Precapture sequence never completed!", precaptureComplete);
836                         assertTrue("Focus sequence never completed!", focusComplete);
837 
838                         // Done
839 
840                         stopCapture(/*fast*/ false);
841                         preview.release();
842 
843                     }
844                 }
845             } finally {
846                 closeDevice(id);
847             }
848         }
849     }
850 
prepareTriggerTestSession( SurfaceTexture preview, int aeMode, int afMode)851     private CaptureRequest.Builder prepareTriggerTestSession(
852             SurfaceTexture preview, int aeMode, int afMode) throws Exception {
853         Log.i(TAG, String.format("Testing AE mode %s, AF mode %s",
854                         StaticMetadata.AE_MODE_NAMES[aeMode],
855                         StaticMetadata.AF_MODE_NAMES[afMode]));
856 
857 
858         Surface previewSurface = new Surface(preview);
859 
860         preview.setDefaultBufferSize(640, 480);
861 
862         ArrayList<Surface> sessionOutputs = new ArrayList<>();
863         sessionOutputs.add(previewSurface);
864 
865         createSession(sessionOutputs);
866 
867         CaptureRequest.Builder previewRequest =
868                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
869 
870         previewRequest.addTarget(previewSurface);
871 
872         previewRequest.set(CaptureRequest.CONTROL_AE_MODE, aeMode);
873         previewRequest.set(CaptureRequest.CONTROL_AF_MODE, afMode);
874 
875         return previewRequest;
876     }
877 
cancelTriggersAndWait(CaptureRequest.Builder previewRequest, SimpleCaptureCallback captureListener, int afMode)878     private void cancelTriggersAndWait(CaptureRequest.Builder previewRequest,
879             SimpleCaptureCallback captureListener, int afMode) throws Exception {
880         previewRequest.set(CaptureRequest.CONTROL_AF_TRIGGER,
881                 CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
882         previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
883                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
884 
885         CaptureRequest triggerRequest = previewRequest.build();
886         mCameraSession.capture(triggerRequest, captureListener, mHandler);
887 
888         // Wait for a few frames to initialize 3A
889 
890         CaptureResult previewResult = null;
891         int afState;
892         int aeState;
893 
894         for (int i = 0; i < PREVIEW_WARMUP_FRAMES; i++) {
895             previewResult = captureListener.getCaptureResult(
896                     CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
897             if (VERBOSE) {
898                 afState = previewResult.get(CaptureResult.CONTROL_AF_STATE);
899                 aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE);
900                 Log.v(TAG, String.format("AF state: %s, AE state: %s",
901                                 StaticMetadata.AF_STATE_NAMES[afState],
902                                 StaticMetadata.AE_STATE_NAMES[aeState]));
903             }
904         }
905 
906         // Verify starting states
907 
908         afState = previewResult.get(CaptureResult.CONTROL_AF_STATE);
909         aeState = previewResult.get(CaptureResult.CONTROL_AE_STATE);
910 
911         switch (afMode) {
912             case CaptureResult.CONTROL_AF_MODE_AUTO:
913             case CaptureResult.CONTROL_AF_MODE_MACRO:
914                 assertTrue(String.format("AF state not INACTIVE, is %s",
915                                 StaticMetadata.AF_STATE_NAMES[afState]),
916                         afState == CaptureResult.CONTROL_AF_STATE_INACTIVE);
917                 break;
918             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE:
919             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO:
920                 // After several frames, AF must no longer be in INACTIVE state
921                 assertTrue(String.format("In AF mode %s, AF state not PASSIVE_SCAN" +
922                                 ", PASSIVE_FOCUSED, or PASSIVE_UNFOCUSED, is %s",
923                                 StaticMetadata.AF_MODE_NAMES[afMode],
924                                 StaticMetadata.AF_STATE_NAMES[afState]),
925                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN ||
926                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED ||
927                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED);
928                 break;
929             default:
930                 fail("unexpected af mode");
931         }
932 
933         // After several frames, AE must no longer be in INACTIVE state
934         assertTrue(String.format("AE state must be SEARCHING, CONVERGED, " +
935                         "or FLASH_REQUIRED, is %s", StaticMetadata.AE_STATE_NAMES[aeState]),
936                 aeState == CaptureResult.CONTROL_AE_STATE_SEARCHING ||
937                 aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
938                 aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED);
939     }
940 
verifyAfSequence(int afMode, int afState, boolean focusComplete)941     private boolean verifyAfSequence(int afMode, int afState, boolean focusComplete) {
942         if (focusComplete) {
943             assertTrue(String.format("AF Mode %s: Focus lock lost after convergence: AF state: %s",
944                             StaticMetadata.AF_MODE_NAMES[afMode],
945                             StaticMetadata.AF_STATE_NAMES[afState]),
946                     afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
947                     afState ==CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
948             return focusComplete;
949         }
950         if (VERBOSE) {
951             Log.v(TAG, String.format("AF mode: %s, AF state: %s",
952                             StaticMetadata.AF_MODE_NAMES[afMode],
953                             StaticMetadata.AF_STATE_NAMES[afState]));
954         }
955         switch (afMode) {
956             case CaptureResult.CONTROL_AF_MODE_AUTO:
957             case CaptureResult.CONTROL_AF_MODE_MACRO:
958                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
959                                 StaticMetadata.AF_MODE_NAMES[afMode],
960                                 StaticMetadata.AF_STATE_NAMES[afState]),
961                         afState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN ||
962                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
963                         afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
964                 focusComplete =
965                         (afState != CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN);
966                 break;
967             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE:
968                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
969                                 StaticMetadata.AF_MODE_NAMES[afMode],
970                                 StaticMetadata.AF_STATE_NAMES[afState]),
971                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN ||
972                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
973                         afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
974                 focusComplete =
975                         (afState != CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN);
976                 break;
977             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO:
978                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
979                                 StaticMetadata.AF_MODE_NAMES[afMode],
980                                 StaticMetadata.AF_STATE_NAMES[afState]),
981                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
982                         afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
983                 focusComplete = true;
984                 break;
985             default:
986                 fail("Unexpected AF mode: " + StaticMetadata.AF_MODE_NAMES[afMode]);
987         }
988         return focusComplete;
989     }
990 
verifyAeSequence(int aeState, boolean precaptureComplete)991     private boolean verifyAeSequence(int aeState, boolean precaptureComplete) {
992         if (precaptureComplete) {
993             assertTrue("Precapture state seen after convergence",
994                     aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE);
995             return precaptureComplete;
996         }
997         if (VERBOSE) {
998             Log.v(TAG, String.format("AE state: %s", StaticMetadata.AE_STATE_NAMES[aeState]));
999         }
1000         switch (aeState) {
1001             case CaptureResult.CONTROL_AE_STATE_PRECAPTURE:
1002                 // scan still continuing
1003                 break;
1004             case CaptureResult.CONTROL_AE_STATE_CONVERGED:
1005             case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED:
1006                 // completed
1007                 precaptureComplete = true;
1008                 break;
1009             default:
1010                 fail(String.format("Precapture sequence transitioned to "
1011                                 + "state %s incorrectly!", StaticMetadata.AE_STATE_NAMES[aeState]));
1012                 break;
1013         }
1014         return precaptureComplete;
1015     }
1016 
1017     /**
1018      * Sanity check the configuration tables.
1019      */
sanityCheckConfigurationTables(final int[][][] tables)1020     private void sanityCheckConfigurationTables(final int[][][] tables) throws Exception {
1021         int tableIdx = 0;
1022         for (int[][] table : tables) {
1023             int rowIdx = 0;
1024             for (int[] row : table) {
1025                 assertTrue(String.format("Odd number of entries for table %d row %d: %s ",
1026                                 tableIdx, rowIdx, Arrays.toString(row)),
1027                         (row.length % 2) == 0);
1028                 for (int i = 0; i < row.length; i += 2) {
1029                     int format = row[i];
1030                     int maxSize = row[i + 1];
1031                     assertTrue(String.format("table %d row %d index %d format not valid: %d",
1032                                     tableIdx, rowIdx, i, format),
1033                             format == PRIV || format == JPEG || format == YUV || format == RAW);
1034                     assertTrue(String.format("table %d row %d index %d max size not valid: %d",
1035                                     tableIdx, rowIdx, i + 1, maxSize),
1036                             maxSize == PREVIEW || maxSize == RECORD ||
1037                             maxSize == MAXIMUM || maxSize == VGA);
1038                 }
1039                 rowIdx++;
1040             }
1041             tableIdx++;
1042         }
1043     }
1044 
1045     /**
1046      * Simple holder for resolutions to use for different camera outputs and size limits.
1047      */
1048     static class MaxStreamSizes {
1049         // Format shorthands
1050         static final int PRIV = ImageFormat.PRIVATE;
1051         static final int JPEG = ImageFormat.JPEG;
1052         static final int YUV  = ImageFormat.YUV_420_888;
1053         static final int RAW  = ImageFormat.RAW_SENSOR;
1054 
1055         // Max resolution indices
1056         static final int PREVIEW = 0;
1057         static final int RECORD  = 1;
1058         static final int MAXIMUM = 2;
1059         static final int VGA = 3;
1060         static final int RESOLUTION_COUNT = 4;
1061 
MaxStreamSizes(StaticMetadata sm, String cameraId, Context context)1062         public MaxStreamSizes(StaticMetadata sm, String cameraId, Context context) {
1063             Size[] privSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.PRIVATE,
1064                     StaticMetadata.StreamDirection.Output);
1065             Size[] yuvSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888,
1066                     StaticMetadata.StreamDirection.Output);
1067             Size[] jpegSizes = sm.getJpegOutputSizesChecked();
1068             Size[] rawSizes = sm.getRawOutputSizesChecked();
1069 
1070             Size maxPreviewSize = getMaxPreviewSize(context, cameraId);
1071 
1072             maxRawSize = (rawSizes.length != 0) ? CameraTestUtils.getMaxSize(rawSizes) : null;
1073 
1074             if (sm.isColorOutputSupported()) {
1075                 maxPrivSizes[PREVIEW] = getMaxSize(privSizes, maxPreviewSize);
1076                 maxYuvSizes[PREVIEW]  = getMaxSize(yuvSizes, maxPreviewSize);
1077                 maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, maxPreviewSize);
1078 
1079                 maxPrivSizes[RECORD] = getMaxRecordingSize(cameraId);
1080                 maxYuvSizes[RECORD]  = getMaxRecordingSize(cameraId);
1081                 maxJpegSizes[RECORD] = getMaxRecordingSize(cameraId);
1082 
1083                 maxPrivSizes[MAXIMUM] = CameraTestUtils.getMaxSize(privSizes);
1084                 maxYuvSizes[MAXIMUM] = CameraTestUtils.getMaxSize(yuvSizes);
1085                 maxJpegSizes[MAXIMUM] = CameraTestUtils.getMaxSize(jpegSizes);
1086 
1087                 // Must always be supported, add unconditionally
1088                 final Size vgaSize = new Size(640, 480);
1089                 maxPrivSizes[VGA] = vgaSize;
1090                 maxYuvSizes[VGA] = vgaSize;
1091                 maxJpegSizes[VGA] = vgaSize;
1092             }
1093 
1094             StreamConfigurationMap configs = sm.getCharacteristics().get(
1095                     CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
1096             Size[] privInputSizes = configs.getInputSizes(ImageFormat.PRIVATE);
1097             maxInputPrivSize = privInputSizes != null ?
1098                     CameraTestUtils.getMaxSize(privInputSizes) : null;
1099             Size[] yuvInputSizes = configs.getInputSizes(ImageFormat.YUV_420_888);
1100             maxInputYuvSize = yuvInputSizes != null ?
1101                     CameraTestUtils.getMaxSize(yuvInputSizes) : null;
1102 
1103         }
1104 
1105         public final Size[] maxPrivSizes = new Size[RESOLUTION_COUNT];
1106         public final Size[] maxJpegSizes = new Size[RESOLUTION_COUNT];
1107         public final Size[] maxYuvSizes = new Size[RESOLUTION_COUNT];
1108         public final Size maxRawSize;
1109         // TODO: support non maximum reprocess input.
1110         public final Size maxInputPrivSize;
1111         public final Size maxInputYuvSize;
1112 
configToString(int[] config)1113         static public String configToString(int[] config) {
1114             StringBuilder b = new StringBuilder("{ ");
1115             for (int i = 0; i < config.length; i += 2) {
1116                 int format = config[i];
1117                 int sizeLimit = config[i + 1];
1118 
1119                 appendFormatSize(b, format, sizeLimit);
1120                 b.append(" ");
1121             }
1122             b.append("}");
1123             return b.toString();
1124         }
1125 
reprocessConfigToString(int[] reprocessConfig)1126         static public String reprocessConfigToString(int[] reprocessConfig) {
1127             // reprocessConfig[0..1] is the input configuration
1128             StringBuilder b = new StringBuilder("Input: ");
1129             appendFormatSize(b, reprocessConfig[0], reprocessConfig[1]);
1130 
1131             // reprocessConfig[0..1] is also output configuration to be captured as reprocess input.
1132             b.append(", Outputs: { ");
1133             for (int i = 0; i < reprocessConfig.length; i += 2) {
1134                 int format = reprocessConfig[i];
1135                 int sizeLimit = reprocessConfig[i + 1];
1136 
1137                 appendFormatSize(b, format, sizeLimit);
1138                 b.append(" ");
1139             }
1140             b.append("}");
1141             return b.toString();
1142         }
1143 
appendFormatSize(StringBuilder b, int format, int Size)1144         static private void appendFormatSize(StringBuilder b, int format, int Size) {
1145             switch (format) {
1146                 case PRIV:
1147                     b.append("[PRIV, ");
1148                     break;
1149                 case JPEG:
1150                     b.append("[JPEG, ");
1151                     break;
1152                 case YUV:
1153                     b.append("[YUV, ");
1154                     break;
1155                 case RAW:
1156                     b.append("[RAW, ");
1157                     break;
1158                 default:
1159                     b.append("[UNK, ");
1160                     break;
1161             }
1162 
1163             switch (Size) {
1164                 case PREVIEW:
1165                     b.append("PREVIEW]");
1166                     break;
1167                 case RECORD:
1168                     b.append("RECORD]");
1169                     break;
1170                 case MAXIMUM:
1171                     b.append("MAXIMUM]");
1172                     break;
1173                 case VGA:
1174                     b.append("VGA]");
1175                     break;
1176                 default:
1177                     b.append("UNK]");
1178                     break;
1179             }
1180         }
1181     }
1182 
1183     /**
1184      * Return an InputConfiguration for a given reprocess configuration.
1185      */
getInputConfig(int[] reprocessConfig, MaxStreamSizes maxSizes)1186     private InputConfiguration getInputConfig(int[] reprocessConfig, MaxStreamSizes maxSizes) {
1187         int format;
1188         Size size;
1189 
1190         if (reprocessConfig[1] != MAXIMUM) {
1191             throw new IllegalArgumentException("Test only supports MAXIMUM input");
1192         }
1193 
1194         switch (reprocessConfig[0]) {
1195             case PRIV:
1196                 format = ImageFormat.PRIVATE;
1197                 size = maxSizes.maxInputPrivSize;
1198                 break;
1199             case YUV:
1200                 format = ImageFormat.YUV_420_888;
1201                 size = maxSizes.maxInputYuvSize;
1202                 break;
1203             default:
1204                 throw new IllegalArgumentException("Input format not supported: " +
1205                         reprocessConfig[0]);
1206         }
1207 
1208         return new InputConfiguration(size.getWidth(), size.getHeight(), format);
1209     }
1210 
testReprocessStreamCombination(String cameraId, int[] reprocessConfig, MaxStreamSizes maxSizes, StaticMetadata staticInfo)1211     private void testReprocessStreamCombination(String cameraId, int[] reprocessConfig,
1212             MaxStreamSizes maxSizes, StaticMetadata staticInfo) throws Exception {
1213 
1214         Log.i(TAG, String.format("Testing Camera %s, reprocess config: %s", cameraId,
1215                 MaxStreamSizes.reprocessConfigToString(reprocessConfig)));
1216 
1217         final int TIMEOUT_FOR_RESULT_MS = 3000;
1218         final int NUM_REPROCESS_CAPTURES_PER_CONFIG = 3;
1219 
1220         List<SurfaceTexture> privTargets = new ArrayList<>();
1221         List<ImageReader> jpegTargets = new ArrayList<>();
1222         List<ImageReader> yuvTargets = new ArrayList<>();
1223         List<ImageReader> rawTargets = new ArrayList<>();
1224         List<Surface> outputSurfaces = new ArrayList<>();
1225         ImageReader inputReader = null;
1226         ImageWriter inputWriter = null;
1227         SimpleImageReaderListener inputReaderListener = new SimpleImageReaderListener();
1228         SimpleCaptureCallback inputCaptureListener = new SimpleCaptureCallback();
1229         SimpleCaptureCallback reprocessOutputCaptureListener = new SimpleCaptureCallback();
1230 
1231         boolean supportYuvReprocess = staticInfo.isCapabilitySupported(
1232                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
1233         boolean supportOpaqueReprocess = staticInfo.isCapabilitySupported(
1234                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
1235 
1236         // Skip the configuration if the format is not supported for reprocessing.
1237         if ((reprocessConfig[0] == YUV && !supportYuvReprocess) ||
1238                 (reprocessConfig[0] == PRIV && !supportOpaqueReprocess)) {
1239             return;
1240         }
1241 
1242         try {
1243             // reprocessConfig[2..] are additional outputs
1244             setupConfigurationTargets(
1245                     Arrays.copyOfRange(reprocessConfig, 2, reprocessConfig.length),
1246                     maxSizes, privTargets, jpegTargets, yuvTargets, rawTargets, outputSurfaces,
1247                     NUM_REPROCESS_CAPTURES_PER_CONFIG);
1248 
1249             // reprocessConfig[0:1] is input
1250             InputConfiguration inputConfig = getInputConfig(
1251                     Arrays.copyOfRange(reprocessConfig, 0, 2), maxSizes);
1252 
1253             // For each config, YUV and JPEG outputs will be tested. (For YUV reprocessing,
1254             // the YUV ImageReader for input is also used for output.)
1255             final int totalNumReprocessCaptures =  NUM_REPROCESS_CAPTURES_PER_CONFIG * (
1256                     (inputConfig.getFormat() == ImageFormat.YUV_420_888 ? 1 : 0) +
1257                     jpegTargets.size() + yuvTargets.size());
1258 
1259             // It needs 1 input buffer for each reprocess capture + the number of buffers
1260             // that will be used as outputs.
1261             inputReader = ImageReader.newInstance(inputConfig.getWidth(), inputConfig.getHeight(),
1262                     inputConfig.getFormat(),
1263                     totalNumReprocessCaptures + NUM_REPROCESS_CAPTURES_PER_CONFIG);
1264             inputReader.setOnImageAvailableListener(inputReaderListener, mHandler);
1265             outputSurfaces.add(inputReader.getSurface());
1266 
1267             // Verify we can create a reprocessable session with the input and all outputs.
1268             BlockingSessionCallback sessionListener = new BlockingSessionCallback();
1269             CameraCaptureSession session = configureReprocessableCameraSession(mCamera,
1270                     inputConfig, outputSurfaces, sessionListener, mHandler);
1271             inputWriter = ImageWriter.newInstance(session.getInputSurface(),
1272                     totalNumReprocessCaptures);
1273 
1274             // Prepare a request for reprocess input
1275             CaptureRequest.Builder builder = mCamera.createCaptureRequest(
1276                     CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
1277             builder.addTarget(inputReader.getSurface());
1278 
1279             for (int i = 0; i < totalNumReprocessCaptures; i++) {
1280                 session.capture(builder.build(), inputCaptureListener, mHandler);
1281             }
1282 
1283             List<CaptureRequest> reprocessRequests = new ArrayList<>();
1284             List<Surface> reprocessOutputs = new ArrayList<>();
1285             if (inputConfig.getFormat() == ImageFormat.YUV_420_888) {
1286                 reprocessOutputs.add(inputReader.getSurface());
1287             }
1288 
1289             for (ImageReader reader : jpegTargets) {
1290                 reprocessOutputs.add(reader.getSurface());
1291             }
1292 
1293             for (ImageReader reader : yuvTargets) {
1294                 reprocessOutputs.add(reader.getSurface());
1295             }
1296 
1297             for (int i = 0; i < NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) {
1298                 for (Surface output : reprocessOutputs) {
1299                     TotalCaptureResult result = inputCaptureListener.getTotalCaptureResult(
1300                             TIMEOUT_FOR_RESULT_MS);
1301                     builder =  mCamera.createReprocessCaptureRequest(result);
1302                     inputWriter.queueInputImage(
1303                             inputReaderListener.getImage(TIMEOUT_FOR_RESULT_MS));
1304                     builder.addTarget(output);
1305                     reprocessRequests.add(builder.build());
1306                 }
1307             }
1308 
1309             session.captureBurst(reprocessRequests, reprocessOutputCaptureListener, mHandler);
1310 
1311             for (int i = 0; i < reprocessOutputs.size() * NUM_REPROCESS_CAPTURES_PER_CONFIG; i++) {
1312                 TotalCaptureResult result = reprocessOutputCaptureListener.getTotalCaptureResult(
1313                         TIMEOUT_FOR_RESULT_MS);
1314             }
1315         } catch (Throwable e) {
1316             mCollector.addMessage(String.format("Reprocess stream combination %s failed due to: %s",
1317                     MaxStreamSizes.reprocessConfigToString(reprocessConfig), e.getMessage()));
1318         } finally {
1319             inputReaderListener.drain();
1320             reprocessOutputCaptureListener.drain();
1321 
1322             for (SurfaceTexture target : privTargets) {
1323                 target.release();
1324             }
1325 
1326             for (ImageReader target : jpegTargets) {
1327                 target.close();
1328             }
1329 
1330             for (ImageReader target : yuvTargets) {
1331                 target.close();
1332             }
1333 
1334             for (ImageReader target : rawTargets) {
1335                 target.close();
1336             }
1337 
1338             if (inputReader != null) {
1339                 inputReader.close();
1340             }
1341 
1342             if (inputWriter != null) {
1343                 inputWriter.close();
1344             }
1345         }
1346     }
1347 
testOutputCombination(String cameraId, int[] config, MaxStreamSizes maxSizes)1348     private void testOutputCombination(String cameraId, int[] config, MaxStreamSizes maxSizes)
1349             throws Exception {
1350 
1351         Log.i(TAG, String.format("Testing Camera %s, config %s",
1352                         cameraId, MaxStreamSizes.configToString(config)));
1353 
1354         // Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS
1355         final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000;
1356         final int MIN_RESULT_COUNT = 3;
1357 
1358         // Set up outputs
1359         List<Surface> outputSurfaces = new ArrayList<Surface>();
1360         List<SurfaceTexture> privTargets = new ArrayList<SurfaceTexture>();
1361         List<ImageReader> jpegTargets = new ArrayList<ImageReader>();
1362         List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
1363         List<ImageReader> rawTargets = new ArrayList<ImageReader>();
1364 
1365         setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
1366                 rawTargets, outputSurfaces, MIN_RESULT_COUNT);
1367 
1368         boolean haveSession = false;
1369         try {
1370             CaptureRequest.Builder requestBuilder =
1371                     mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
1372 
1373             for (Surface s : outputSurfaces) {
1374                 requestBuilder.addTarget(s);
1375             }
1376 
1377             CameraCaptureSession.CaptureCallback mockCaptureCallback =
1378                     mock(CameraCaptureSession.CaptureCallback.class);
1379 
1380             createSession(outputSurfaces);
1381             haveSession = true;
1382             CaptureRequest request = requestBuilder.build();
1383             mCameraSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
1384 
1385             verify(mockCaptureCallback,
1386                     timeout(TIMEOUT_FOR_RESULT_MS * MIN_RESULT_COUNT).atLeast(MIN_RESULT_COUNT))
1387                     .onCaptureCompleted(
1388                         eq(mCameraSession),
1389                         eq(request),
1390                         isA(TotalCaptureResult.class));
1391             verify(mockCaptureCallback, never()).
1392                     onCaptureFailed(
1393                         eq(mCameraSession),
1394                         eq(request),
1395                         isA(CaptureFailure.class));
1396 
1397         } catch (Throwable e) {
1398             mCollector.addMessage(String.format("Output combination %s failed due to: %s",
1399                     MaxStreamSizes.configToString(config), e.getMessage()));
1400         }
1401         if (haveSession) {
1402             try {
1403                 Log.i(TAG, String.format("Done with camera %s, config %s, closing session",
1404                                 cameraId, MaxStreamSizes.configToString(config)));
1405                 stopCapture(/*fast*/false);
1406             } catch (Throwable e) {
1407                 mCollector.addMessage(
1408                     String.format("Closing down for output combination %s failed due to: %s",
1409                             MaxStreamSizes.configToString(config), e.getMessage()));
1410             }
1411         }
1412 
1413         for (SurfaceTexture target : privTargets) {
1414             target.release();
1415         }
1416         for (ImageReader target : jpegTargets) {
1417             target.close();
1418         }
1419         for (ImageReader target : yuvTargets) {
1420             target.close();
1421         }
1422         for (ImageReader target : rawTargets) {
1423             target.close();
1424         }
1425     }
1426 
setupConfigurationTargets(int[] outputConfigs, MaxStreamSizes maxSizes, List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets, List<ImageReader> yuvTargets, List<ImageReader> rawTargets, List<Surface> outputSurfaces, int numBuffers)1427     private void setupConfigurationTargets(int[] outputConfigs, MaxStreamSizes maxSizes,
1428             List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
1429             List<ImageReader> yuvTargets, List<ImageReader> rawTargets,
1430             List<Surface> outputSurfaces, int numBuffers) {
1431 
1432         ImageDropperListener imageDropperListener = new ImageDropperListener();
1433 
1434         for (int i = 0; i < outputConfigs.length; i += 2) {
1435             int format = outputConfigs[i];
1436             int sizeLimit = outputConfigs[i + 1];
1437 
1438             switch (format) {
1439                 case PRIV: {
1440                     Size targetSize = maxSizes.maxPrivSizes[sizeLimit];
1441                     SurfaceTexture target = new SurfaceTexture(/*random int*/1);
1442                     target.setDefaultBufferSize(targetSize.getWidth(), targetSize.getHeight());
1443                     outputSurfaces.add(new Surface(target));
1444                     privTargets.add(target);
1445                     break;
1446                 }
1447                 case JPEG: {
1448                     Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
1449                     ImageReader target = ImageReader.newInstance(
1450                         targetSize.getWidth(), targetSize.getHeight(), JPEG, numBuffers);
1451                     target.setOnImageAvailableListener(imageDropperListener, mHandler);
1452                     outputSurfaces.add(target.getSurface());
1453                     jpegTargets.add(target);
1454                     break;
1455                 }
1456                 case YUV: {
1457                     Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
1458                     ImageReader target = ImageReader.newInstance(
1459                         targetSize.getWidth(), targetSize.getHeight(), YUV, numBuffers);
1460                     target.setOnImageAvailableListener(imageDropperListener, mHandler);
1461                     outputSurfaces.add(target.getSurface());
1462                     yuvTargets.add(target);
1463                     break;
1464                 }
1465                 case RAW: {
1466                     Size targetSize = maxSizes.maxRawSize;
1467                     ImageReader target = ImageReader.newInstance(
1468                         targetSize.getWidth(), targetSize.getHeight(), RAW, numBuffers);
1469                     target.setOnImageAvailableListener(imageDropperListener, mHandler);
1470                     outputSurfaces.add(target.getSurface());
1471                     rawTargets.add(target);
1472                     break;
1473                 }
1474                 default:
1475                     fail("Unknown output format " + format);
1476             }
1477         }
1478     }
1479 
getMaxRecordingSize(String cameraId)1480     private static Size getMaxRecordingSize(String cameraId) {
1481         int id = Integer.valueOf(cameraId);
1482 
1483         int quality =
1484                 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_2160P) ?
1485                     CamcorderProfile.QUALITY_2160P :
1486                 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_1080P) ?
1487                     CamcorderProfile.QUALITY_1080P :
1488                 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_720P) ?
1489                     CamcorderProfile.QUALITY_720P :
1490                 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_480P) ?
1491                     CamcorderProfile.QUALITY_480P :
1492                 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_QVGA) ?
1493                     CamcorderProfile.QUALITY_QVGA :
1494                 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_CIF) ?
1495                     CamcorderProfile.QUALITY_CIF :
1496                 CamcorderProfile.hasProfile(id, CamcorderProfile.QUALITY_QCIF) ?
1497                     CamcorderProfile.QUALITY_QCIF :
1498                     -1;
1499 
1500         assertTrue("No recording supported for camera id " + cameraId, quality != -1);
1501 
1502         CamcorderProfile maxProfile = CamcorderProfile.get(id, quality);
1503         return new Size(maxProfile.videoFrameWidth, maxProfile.videoFrameHeight);
1504     }
1505 
1506     /**
1507      * Get maximum size in list that's equal or smaller to than the bound.
1508      * Returns null if no size is smaller than or equal to the bound.
1509      */
getMaxSize(Size[] sizes, Size bound)1510     private static Size getMaxSize(Size[] sizes, Size bound) {
1511         if (sizes == null || sizes.length == 0) {
1512             throw new IllegalArgumentException("sizes was empty");
1513         }
1514 
1515         Size sz = null;
1516         for (Size size : sizes) {
1517             if (size.getWidth() <= bound.getWidth() && size.getHeight() <= bound.getHeight()) {
1518 
1519                 if (sz == null) {
1520                     sz = size;
1521                 } else {
1522                     long curArea = sz.getWidth() * (long) sz.getHeight();
1523                     long newArea = size.getWidth() * (long) size.getHeight();
1524                     if ( newArea > curArea ) {
1525                         sz = size;
1526                     }
1527                 }
1528             }
1529         }
1530 
1531         assertTrue("No size under bound found: " + Arrays.toString(sizes) + " bound " + bound,
1532                 sz != null);
1533 
1534         return sz;
1535     }
1536 
getMaxPreviewSize(Context context, String cameraId)1537     private static Size getMaxPreviewSize(Context context, String cameraId) {
1538         try {
1539             WindowManager windowManager =
1540                 (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
1541             Display display = windowManager.getDefaultDisplay();
1542 
1543             int width = display.getWidth();
1544             int height = display.getHeight();
1545 
1546             if (height > width) {
1547                 height = width;
1548                 width = display.getHeight();
1549             }
1550 
1551             CameraManager camMgr =
1552                 (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
1553             List<Size> orderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes(
1554                 cameraId, camMgr, PREVIEW_SIZE_BOUND);
1555 
1556             if (orderedPreviewSizes != null) {
1557                 for (Size size : orderedPreviewSizes) {
1558                     if (width >= size.getWidth() &&
1559                         height >= size.getHeight())
1560                         return size;
1561                 }
1562             }
1563         } catch (Exception e) {
1564             Log.e(TAG, "getMaxPreviewSize Failed. "+e.toString());
1565         }
1566         return PREVIEW_SIZE_BOUND;
1567     }
1568 }
1569