1 /*
2  * Copyright 2013 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 android.content.Context;
20 import android.graphics.ImageFormat;
21 import android.hardware.camera2.CameraCaptureSession;
22 import android.hardware.camera2.CameraCharacteristics;
23 import android.hardware.camera2.CameraDevice;
24 import android.hardware.camera2.CaptureRequest;
25 import android.hardware.camera2.CaptureResult;
26 import android.hardware.camera2.TotalCaptureResult;
27 import android.media.Image;
28 import android.media.ImageReader;
29 import android.os.SystemClock;
30 import android.util.Pair;
31 import android.util.Size;
32 import android.hardware.camera2.cts.helpers.StaticMetadata;
33 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
34 
35 import static android.hardware.camera2.cts.CameraTestUtils.*;
36 import static android.hardware.camera2.cts.helpers.CameraSessionUtils.*;
37 
38 import android.util.Log;
39 import android.view.Surface;
40 
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Set;
47 import java.util.concurrent.LinkedBlockingQueue;
48 import java.util.concurrent.TimeUnit;
49 
50 public class CaptureResultTest extends Camera2AndroidTestCase {
51     private static final String TAG = "CaptureResultTest";
52     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
53     private static final int MAX_NUM_IMAGES = MAX_READER_IMAGES;
54     private static final int NUM_FRAMES_VERIFIED = 30;
55     private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
56 
57     // List that includes all public keys from CaptureResult
58     List<CaptureResult.Key<?>> mAllKeys;
59 
60     // List tracking the failed test keys.
61 
62     @Override
setContext(Context context)63     public void setContext(Context context) {
64         mAllKeys = getAllCaptureResultKeys();
65         super.setContext(context);
66 
67         /**
68          * Workaround for mockito and JB-MR2 incompatibility
69          *
70          * Avoid java.lang.IllegalArgumentException: dexcache == null
71          * https://code.google.com/p/dexmaker/issues/detail?id=2
72          */
73         System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
74     }
75 
76     @Override
setUp()77     protected void setUp() throws Exception {
78         super.setUp();
79     }
80 
81     @Override
tearDown()82     protected void tearDown() throws Exception {
83         super.tearDown();
84     }
85 
86     /**
87      * <p>
88      * Basic non-null check test for multiple capture results.
89      * </p>
90      * <p>
91      * When capturing many frames, some camera devices may return some results that have null keys
92      * randomly, which is an API violation and could cause application crash randomly. This test
93      * runs a typical flexible yuv capture many times, and checks if there is any null entries in
94      * a capture result.
95      * </p>
96      */
testCameraCaptureResultAllKeys()97     public void testCameraCaptureResultAllKeys() throws Exception {
98         for (String id : mCameraIds) {
99             try {
100                 openDevice(id);
101                 if (mStaticInfo.isColorOutputSupported()) {
102                     // Create image reader and surface.
103                     Size size = mOrderedPreviewSizes.get(0);
104                     createDefaultImageReader(size, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
105                             new ImageDropperListener());
106                 } else {
107                     Size size = getMaxDepthSize(id, mCameraManager);
108                     createDefaultImageReader(size, ImageFormat.DEPTH16, MAX_NUM_IMAGES,
109                             new ImageDropperListener());
110                 }
111 
112                 // Configure output streams.
113                 List<Surface> outputSurfaces = new ArrayList<Surface>(1);
114                 outputSurfaces.add(mReaderSurface);
115                 createSession(outputSurfaces);
116 
117                 CaptureRequest.Builder requestBuilder =
118                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
119                 assertNotNull("Failed to create capture request", requestBuilder);
120                 requestBuilder.addTarget(mReaderSurface);
121 
122                 // Start capture
123                 SimpleCaptureCallback captureListener = new SimpleCaptureCallback();
124                 startCapture(requestBuilder.build(), /*repeating*/true, captureListener, mHandler);
125 
126                 // Get the waived keys for current camera device
127                 List<CaptureResult.Key<?>> waiverkeys = getWaiverKeysForCamera();
128 
129                 // Verify results
130                 validateCaptureResult(captureListener, waiverkeys, requestBuilder,
131                         NUM_FRAMES_VERIFIED);
132 
133                 stopCapture(/*fast*/false);
134             } finally {
135                 closeDevice(id);
136                 closeDefaultImageReader();
137             }
138         }
139     }
140 
141     /**
142      * Check partial results conform to its specification.
143      * <p>
144      * The test is skipped if partial result is not supported on device. </p>
145      * <p>Test summary:<ul>
146      * <li>1. Number of partial results is less than or equal to
147      * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT}.
148      * <li>2. Each key appeared in partial results must be unique across all partial results.
149      * <li>3. All keys appeared in partial results must be present in TotalCaptureResult
150      * <li>4. Also test onCaptureComplete callback always happen after onCaptureStart or
151      * onCaptureProgressed callbacks.
152      * </ul></p>
153      */
testPartialResult()154     public void testPartialResult() throws Exception {
155         final int NUM_FRAMES_TESTED = 30;
156         final int WAIT_FOR_RESULT_TIMOUT_MS = 2000;
157         for (String id : mCameraIds) {
158             try {
159                 openDevice(id);
160 
161                 // Skip the test if partial result is not supported
162                 int partialResultCount = mStaticInfo.getPartialResultCount();
163                 if (partialResultCount == 1) {
164                     continue;
165                 }
166 
167                 // Create image reader and surface.
168                 if (mStaticInfo.isColorOutputSupported()) {
169                     Size size = mOrderedPreviewSizes.get(0);
170                     createDefaultImageReader(size, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
171                             new ImageDropperListener());
172                 } else {
173                     Size size = getMaxDepthSize(id, mCameraManager);
174                     createDefaultImageReader(size, ImageFormat.DEPTH16, MAX_NUM_IMAGES,
175                             new ImageDropperListener());
176                 }
177 
178                 // Configure output streams.
179                 List<Surface> outputSurfaces = new ArrayList<Surface>(1);
180                 outputSurfaces.add(mReaderSurface);
181                 createSession(outputSurfaces);
182 
183                 CaptureRequest.Builder requestBuilder =
184                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
185                 assertNotNull("Failed to create capture request", requestBuilder);
186                 requestBuilder.addTarget(mReaderSurface);
187                 TotalAndPartialResultListener listener =
188                         new TotalAndPartialResultListener();
189 
190                 // Start capture
191                 for (Integer frame = 0; frame < NUM_FRAMES_TESTED; frame++) {
192                     // Set a different tag for each request so the listener can group
193                     // partial results by each request
194                     requestBuilder.setTag(frame);
195                     startCapture(
196                             requestBuilder.build(), /*repeating*/false,
197                             listener, mHandler);
198                 }
199 
200                 // Verify capture results
201                 for (int frame = 0; frame < NUM_FRAMES_TESTED; frame++) {
202                     Pair<TotalCaptureResult, List<CaptureResult>> resultPair =
203                             listener.getCaptureResultPairs(WAIT_FOR_RESULT_TIMOUT_MS);
204 
205                     List<CaptureResult> partialResults = resultPair.second;
206 
207                     if (partialResults == null) {
208                         // HAL only sends total result is legal
209                         partialResults = new ArrayList<>();
210                     }
211 
212                     TotalCaptureResult totalResult = resultPair.first;
213 
214                     mCollector.expectLessOrEqual("Too many partial results",
215                             partialResultCount, partialResults.size());
216                     Set<CaptureResult.Key<?>> appearedPartialKeys =
217                             new HashSet<CaptureResult.Key<?>>();
218                     for (CaptureResult partialResult : partialResults) {
219                         List<CaptureResult.Key<?>> partialKeys = partialResult.getKeys();
220                         mCollector.expectValuesUnique("Partial result keys: ", partialKeys);
221                         for (CaptureResult.Key<?> key : partialKeys) {
222                             mCollector.expectTrue(
223                                     String.format("Key %s appears in multiple partial results",
224                                             key.getName()),
225                                     !appearedPartialKeys.contains(key));
226                         }
227                         appearedPartialKeys.addAll(partialKeys);
228                     }
229 
230                     // Test total result against the partial results
231                     List<CaptureResult.Key<?>> totalResultKeys = totalResult.getKeys();
232                     mCollector.expectTrue(
233                             "TotalCaptureResult must be a super set of partial capture results",
234                             totalResultKeys.containsAll(appearedPartialKeys));
235 
236                     List<CaptureResult> totalResultPartials = totalResult.getPartialResults();
237                     mCollector.expectEquals("TotalCaptureResult's partial results must match " +
238                             "the ones observed by #onCaptureProgressed",
239                             partialResults, totalResultPartials);
240 
241                     if (VERBOSE) {
242                         Log.v(TAG, "testPartialResult - Observed " +
243                                 partialResults.size() + "; queried for " +
244                                 totalResultPartials.size());
245                     }
246                 }
247 
248                 int errorCode = listener.getErrorCode();
249                 if ((errorCode & TotalAndPartialResultListener.ERROR_DUPLICATED_REQUEST) != 0) {
250                     mCollector.addMessage("Listener received multiple onCaptureComplete" +
251                             " callback for the same request");
252                 }
253                 if ((errorCode & TotalAndPartialResultListener.ERROR_WRONG_CALLBACK_ORDER) != 0) {
254                     mCollector.addMessage("Listener received onCaptureStart or" +
255                             " onCaptureProgressed after onCaptureComplete");
256                 }
257 
258                 stopCapture(/*fast*/false);
259             } finally {
260                 closeDevice(id);
261                 closeDefaultImageReader();
262             }
263         }
264     }
265 
266     /**
267      * Check that the timestamps passed in the results, buffers, and capture callbacks match for
268      * a single request, and increase monotonically
269      */
testResultTimestamps()270     public void testResultTimestamps() throws Exception {
271         for (String id : mCameraIds) {
272             ImageReader previewReader = null;
273             ImageReader jpegReader = null;
274 
275             SimpleImageReaderListener jpegListener = new SimpleImageReaderListener();
276             SimpleImageReaderListener prevListener = new SimpleImageReaderListener();
277             try {
278                 openDevice(id);
279                 if (!mStaticInfo.isColorOutputSupported()) {
280                     Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
281                     continue;
282                 }
283 
284                 CaptureRequest.Builder previewBuilder =
285                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
286                 CaptureRequest.Builder multiBuilder =
287                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
288 
289                 // Create image reader and surface.
290                 Size previewSize = mOrderedPreviewSizes.get(0);
291                 Size jpegSize = mOrderedStillSizes.get(0);
292 
293                 // Create ImageReaders.
294                 previewReader = makeImageReader(previewSize, ImageFormat.YUV_420_888,
295                         MAX_NUM_IMAGES, prevListener, mHandler);
296                 jpegReader = makeImageReader(jpegSize, ImageFormat.JPEG,
297                         MAX_NUM_IMAGES, jpegListener, mHandler);
298 
299                 // Configure output streams with preview and jpeg streams.
300                 List<Surface> outputSurfaces = new ArrayList<>(Arrays.asList(
301                         previewReader.getSurface(), jpegReader.getSurface()));
302 
303                 SessionListener mockSessionListener = getMockSessionListener();
304 
305                 CameraCaptureSession session = configureAndVerifySession(mockSessionListener,
306                         mCamera, outputSurfaces, mHandler);
307 
308                 // Configure the requests.
309                 previewBuilder.addTarget(previewReader.getSurface());
310                 multiBuilder.addTarget(previewReader.getSurface());
311                 multiBuilder.addTarget(jpegReader.getSurface());
312 
313                 CaptureCallback mockCaptureCallback = getMockCaptureListener();
314 
315                 // Capture targeting only preview
316                 Pair<TotalCaptureResult, Long> result = captureAndVerifyResult(mockCaptureCallback,
317                         session, previewBuilder.build(), mHandler);
318 
319                 // Check if all timestamps are the same
320                 Image prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
321                 validateTimestamps("Result 1", result.first,
322                         prevImage, result.second);
323                 prevImage.close();
324 
325                 // Capture targeting both jpeg and preview
326                 Pair<TotalCaptureResult, Long> result2 = captureAndVerifyResult(mockCaptureCallback,
327                         session, multiBuilder.build(), mHandler);
328 
329                 // Check if all timestamps are the same
330                 prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
331                 Image jpegImage = jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
332                 validateTimestamps("Result 2 Preview", result2.first,
333                         prevImage, result2.second);
334                 validateTimestamps("Result 2 Jpeg", result2.first,
335                         jpegImage, result2.second);
336                 prevImage.close();
337                 jpegImage.close();
338 
339                 // Check if timestamps are increasing
340                 mCollector.expectGreater("Timestamps must be increasing.", result.second,
341                         result2.second);
342 
343                 // Capture two preview frames
344                 long startTime = SystemClock.elapsedRealtimeNanos();
345                 Pair<TotalCaptureResult, Long> result3 = captureAndVerifyResult(mockCaptureCallback,
346                         session, previewBuilder.build(), mHandler);
347                 Pair<TotalCaptureResult, Long> result4 = captureAndVerifyResult(mockCaptureCallback,
348                         session, previewBuilder.build(), mHandler);
349                 long clockDiff = SystemClock.elapsedRealtimeNanos() - startTime;
350                 long resultDiff = result4.second - result3.second;
351 
352                 // Check if all timestamps are the same
353                 prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
354                 validateTimestamps("Result 3", result3.first,
355                         prevImage, result3.second);
356                 prevImage.close();
357                 prevImage = prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
358                 validateTimestamps("Result 4", result4.first,
359                         prevImage, result4.second);
360                 prevImage.close();
361 
362                 // Check that the timestamps monotonically increase at a reasonable rate
363                 mCollector.expectGreaterOrEqual("Timestamps increase faster than system clock.",
364                         resultDiff, clockDiff);
365                 mCollector.expectGreater("Timestamps must be increasing.", result3.second,
366                         result4.second);
367             } finally {
368                 closeDevice(id);
369                 closeImageReader(previewReader);
370                 closeImageReader(jpegReader);
371             }
372         }
373     }
374 
validateTimestamps(String msg, TotalCaptureResult result, Image resultImage, long captureTime)375     private void validateTimestamps(String msg, TotalCaptureResult result, Image resultImage,
376                                     long captureTime) {
377         mCollector.expectKeyValueEquals(result, CaptureResult.SENSOR_TIMESTAMP, captureTime);
378         mCollector.expectEquals(msg + ": Capture timestamp must be same as resultImage timestamp",
379                 resultImage.getTimestamp(), captureTime);
380     }
381 
validateCaptureResult(SimpleCaptureCallback captureListener, List<CaptureResult.Key<?>> skippedKeys, CaptureRequest.Builder requestBuilder, int numFramesVerified)382     private void validateCaptureResult(SimpleCaptureCallback captureListener,
383             List<CaptureResult.Key<?>> skippedKeys, CaptureRequest.Builder requestBuilder,
384             int numFramesVerified) throws Exception {
385         CaptureResult result = null;
386         for (int i = 0; i < numFramesVerified; i++) {
387             String failMsg = "Failed capture result " + i + " test ";
388             result = captureListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
389 
390             for (CaptureResult.Key<?> key : mAllKeys) {
391                 if (!skippedKeys.contains(key)) {
392                     /**
393                      * Check the critical tags here.
394                      * TODO: Can use the same key for request and result when request/result
395                      * becomes symmetric (b/14059883). Then below check can be wrapped into
396                      * a generic function.
397                      */
398                     String msg = failMsg + "for key " + key.getName();
399                     if (key.equals(CaptureResult.CONTROL_AE_MODE)) {
400                         mCollector.expectEquals(msg,
401                                 requestBuilder.get(CaptureRequest.CONTROL_AE_MODE),
402                                 result.get(CaptureResult.CONTROL_AE_MODE));
403                     } else if (key.equals(CaptureResult.CONTROL_AF_MODE)) {
404                         mCollector.expectEquals(msg,
405                                 requestBuilder.get(CaptureRequest.CONTROL_AF_MODE),
406                                 result.get(CaptureResult.CONTROL_AF_MODE));
407                     } else if (key.equals(CaptureResult.CONTROL_AWB_MODE)) {
408                         mCollector.expectEquals(msg,
409                                 requestBuilder.get(CaptureRequest.CONTROL_AWB_MODE),
410                                 result.get(CaptureResult.CONTROL_AWB_MODE));
411                     } else if (key.equals(CaptureResult.CONTROL_MODE)) {
412                         mCollector.expectEquals(msg,
413                                 requestBuilder.get(CaptureRequest.CONTROL_MODE),
414                                 result.get(CaptureResult.CONTROL_MODE));
415                     } else if (key.equals(CaptureResult.STATISTICS_FACE_DETECT_MODE)) {
416                         mCollector.expectEquals(msg,
417                                 requestBuilder.get(CaptureRequest.STATISTICS_FACE_DETECT_MODE),
418                                 result.get(CaptureResult.STATISTICS_FACE_DETECT_MODE));
419                     } else if (key.equals(CaptureResult.NOISE_REDUCTION_MODE)) {
420                         mCollector.expectEquals(msg,
421                                 requestBuilder.get(CaptureRequest.NOISE_REDUCTION_MODE),
422                                 result.get(CaptureResult.NOISE_REDUCTION_MODE));
423                     } else if (key.equals(CaptureResult.NOISE_REDUCTION_MODE)) {
424                         mCollector.expectEquals(msg,
425                                 requestBuilder.get(CaptureRequest.NOISE_REDUCTION_MODE),
426                                 result.get(CaptureResult.NOISE_REDUCTION_MODE));
427                     } else if (key.equals(CaptureResult.REQUEST_PIPELINE_DEPTH)) {
428 
429                     } else {
430                         // Only do non-null check for the rest of keys.
431                         mCollector.expectKeyValueNotNull(failMsg, result, key);
432                     }
433                 } else {
434                     // These keys should always be null
435                     if (key.equals(CaptureResult.CONTROL_AE_REGIONS)) {
436                         mCollector.expectNull(
437                                 "Capture result contains AE regions but aeMaxRegions is 0",
438                                 result.get(CaptureResult.CONTROL_AE_REGIONS));
439                     } else if (key.equals(CaptureResult.CONTROL_AWB_REGIONS)) {
440                         mCollector.expectNull(
441                                 "Capture result contains AWB regions but awbMaxRegions is 0",
442                                 result.get(CaptureResult.CONTROL_AWB_REGIONS));
443                     } else if (key.equals(CaptureResult.CONTROL_AF_REGIONS)) {
444                         mCollector.expectNull(
445                                 "Capture result contains AF regions but afMaxRegions is 0",
446                                 result.get(CaptureResult.CONTROL_AF_REGIONS));
447                     }
448                 }
449             }
450         }
451     }
452 
453     /*
454      * Add waiver keys per camera device hardware level and capability.
455      *
456      * Must be called after camera device is opened.
457      */
getWaiverKeysForCamera()458     private List<CaptureResult.Key<?>> getWaiverKeysForCamera() {
459         List<CaptureResult.Key<?>> waiverKeys = new ArrayList<>();
460 
461         // Global waiver keys
462         waiverKeys.add(CaptureResult.JPEG_GPS_LOCATION);
463         waiverKeys.add(CaptureResult.JPEG_ORIENTATION);
464         waiverKeys.add(CaptureResult.JPEG_QUALITY);
465         waiverKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
466         waiverKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
467 
468         // Keys only present when corresponding control is on are being
469         // verified in its own functional test
470         // Only present in certain tonemap mode. Test in CaptureRequestTest.
471         waiverKeys.add(CaptureResult.TONEMAP_CURVE);
472         waiverKeys.add(CaptureResult.TONEMAP_GAMMA);
473         waiverKeys.add(CaptureResult.TONEMAP_PRESET_CURVE);
474         // Only present when test pattern mode is SOLID_COLOR.
475         // TODO: verify this key in test pattern test later
476         waiverKeys.add(CaptureResult.SENSOR_TEST_PATTERN_DATA);
477         // Only present when STATISTICS_LENS_SHADING_MAP_MODE is ON
478         waiverKeys.add(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
479         // Only present when STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES is ON
480         waiverKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP);
481         // Only present when face detection is on
482         waiverKeys.add(CaptureResult.STATISTICS_FACES);
483         // Only present in reprocessing capture result.
484         waiverKeys.add(CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR);
485 
486         //Keys not required if RAW is not supported
487         if (!mStaticInfo.isCapabilitySupported(
488                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
489             waiverKeys.add(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
490             waiverKeys.add(CaptureResult.SENSOR_GREEN_SPLIT);
491             waiverKeys.add(CaptureResult.SENSOR_NOISE_PROFILE);
492         }
493 
494         //Keys for depth output capability
495         if (!mStaticInfo.isCapabilitySupported(
496                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT)) {
497             waiverKeys.add(CaptureResult.LENS_POSE_ROTATION);
498             waiverKeys.add(CaptureResult.LENS_POSE_TRANSLATION);
499             waiverKeys.add(CaptureResult.LENS_INTRINSIC_CALIBRATION);
500             waiverKeys.add(CaptureResult.LENS_RADIAL_DISTORTION);
501         }
502 
503         // Waived if RAW output is not supported
504         int[] outputFormats = mStaticInfo.getAvailableFormats(
505                 StaticMetadata.StreamDirection.Output);
506         boolean supportRaw = false;
507         for (int format : outputFormats) {
508             if (format == ImageFormat.RAW_SENSOR || format == ImageFormat.RAW10 ||
509                     format == ImageFormat.RAW12 || format == ImageFormat.RAW_PRIVATE) {
510                 supportRaw = true;
511                 break;
512             }
513         }
514         if (!supportRaw) {
515             waiverKeys.add(CaptureResult.CONTROL_POST_RAW_SENSITIVITY_BOOST);
516         }
517 
518         if (mStaticInfo.getAeMaxRegionsChecked() == 0) {
519             waiverKeys.add(CaptureResult.CONTROL_AE_REGIONS);
520         }
521         if (mStaticInfo.getAwbMaxRegionsChecked() == 0) {
522             waiverKeys.add(CaptureResult.CONTROL_AWB_REGIONS);
523         }
524         if (mStaticInfo.getAfMaxRegionsChecked() == 0) {
525             waiverKeys.add(CaptureResult.CONTROL_AF_REGIONS);
526         }
527 
528         // Keys for dynamic black/white levels
529         if (!mStaticInfo.isOpticalBlackRegionSupported()) {
530             waiverKeys.add(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL);
531             waiverKeys.add(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL);
532         }
533 
534         if (mStaticInfo.isHardwareLevelAtLeastFull()) {
535             return waiverKeys;
536         }
537 
538         /*
539          * Hardware Level = LIMITED or LEGACY
540          */
541         // Key not present if certain control is not supported
542         if (!mStaticInfo.isColorCorrectionSupported()) {
543             waiverKeys.add(CaptureResult.COLOR_CORRECTION_GAINS);
544             waiverKeys.add(CaptureResult.COLOR_CORRECTION_MODE);
545             waiverKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM);
546         }
547 
548         if (!mStaticInfo.isManualColorAberrationControlSupported()) {
549             waiverKeys.add(CaptureResult.COLOR_CORRECTION_ABERRATION_MODE);
550         }
551 
552         if (!mStaticInfo.isManualToneMapSupported()) {
553             waiverKeys.add(CaptureResult.TONEMAP_MODE);
554         }
555 
556         if (!mStaticInfo.isEdgeModeControlSupported()) {
557             waiverKeys.add(CaptureResult.EDGE_MODE);
558         }
559 
560         if (!mStaticInfo.isHotPixelMapModeControlSupported()) {
561             waiverKeys.add(CaptureResult.HOT_PIXEL_MODE);
562         }
563 
564         if (!mStaticInfo.isNoiseReductionModeControlSupported()) {
565             waiverKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
566         }
567 
568         if (!mStaticInfo.isManualLensShadingMapSupported()) {
569             waiverKeys.add(CaptureResult.SHADING_MODE);
570         }
571 
572         //Keys not required if neither MANUAL_SENSOR nor READ_SENSOR_SETTINGS is supported
573         if (!mStaticInfo.isCapabilitySupported(
574                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) &&
575             !mStaticInfo.isCapabilitySupported(
576                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
577             waiverKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
578             waiverKeys.add(CaptureResult.SENSOR_SENSITIVITY);
579             waiverKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
580             waiverKeys.add(CaptureResult.LENS_APERTURE);
581         }
582 
583         if (!mStaticInfo.isCapabilitySupported(
584                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
585             waiverKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
586             waiverKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
587             waiverKeys.add(CaptureResult.LENS_FOCUS_RANGE);
588             waiverKeys.add(CaptureResult.LENS_STATE);
589             waiverKeys.add(CaptureResult.LENS_FILTER_DENSITY);
590         }
591 
592         if (mStaticInfo.isHardwareLevelLimited() && mStaticInfo.isColorOutputSupported()) {
593             return waiverKeys;
594         }
595 
596         /*
597          * Hardware Level = LEGACY or no regular output is supported
598          */
599         waiverKeys.add(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
600         waiverKeys.add(CaptureResult.CONTROL_AE_STATE);
601         waiverKeys.add(CaptureResult.CONTROL_AWB_STATE);
602         waiverKeys.add(CaptureResult.FLASH_STATE);
603         waiverKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
604         waiverKeys.add(CaptureResult.SENSOR_ROLLING_SHUTTER_SKEW);
605         waiverKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP_MODE);
606         waiverKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
607         waiverKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE);
608         waiverKeys.add(CaptureResult.CONTROL_AE_TARGET_FPS_RANGE);
609         waiverKeys.add(CaptureResult.CONTROL_AF_TRIGGER);
610 
611         if (mStaticInfo.isHardwareLevelLegacy()) {
612             return waiverKeys;
613         }
614 
615         /*
616          * Regular output not supported, only depth, waive color-output-related keys
617          */
618         waiverKeys.add(CaptureResult.CONTROL_SCENE_MODE);
619         waiverKeys.add(CaptureResult.CONTROL_EFFECT_MODE);
620         waiverKeys.add(CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE);
621         waiverKeys.add(CaptureResult.SENSOR_TEST_PATTERN_MODE);
622         waiverKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
623         waiverKeys.add(CaptureResult.COLOR_CORRECTION_ABERRATION_MODE);
624         waiverKeys.add(CaptureResult.CONTROL_AE_ANTIBANDING_MODE);
625         waiverKeys.add(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION);
626         waiverKeys.add(CaptureResult.CONTROL_AE_LOCK);
627         waiverKeys.add(CaptureResult.CONTROL_AE_MODE);
628         waiverKeys.add(CaptureResult.CONTROL_AF_MODE);
629         waiverKeys.add(CaptureResult.CONTROL_AWB_MODE);
630         waiverKeys.add(CaptureResult.CONTROL_AWB_LOCK);
631         waiverKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE);
632         waiverKeys.add(CaptureResult.FLASH_MODE);
633         waiverKeys.add(CaptureResult.SCALER_CROP_REGION);
634 
635         return waiverKeys;
636     }
637 
638     /**
639      * A capture listener implementation for collecting both partial and total results.
640      *
641      * <p> This is not a full-blown class and has some implicit assumptions. The class groups
642      * capture results by capture request, so the user must guarantee each request this listener
643      * is listening is unique. This class is not thread safe, so don't attach an instance object
644      * with multiple handlers.</p>
645      * */
646     private static class TotalAndPartialResultListener
647             extends CameraCaptureSession.CaptureCallback {
648         static final int ERROR_DUPLICATED_REQUEST = 1 << 0;
649         static final int ERROR_WRONG_CALLBACK_ORDER = 1 << 1;
650 
651         private final LinkedBlockingQueue<Pair<TotalCaptureResult, List<CaptureResult>> > mQueue =
652                 new LinkedBlockingQueue<>();
653         private final HashMap<CaptureRequest, List<CaptureResult>> mPartialResultsMap =
654                 new HashMap<CaptureRequest, List<CaptureResult>>();
655         private final HashSet<CaptureRequest> completedRequests = new HashSet<>();
656         private int errorCode = 0;
657 
658         @Override
onCaptureStarted( CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber)659         public void onCaptureStarted(
660             CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber)
661         {
662             checkCallbackOrder(request);
663             createMapEntryIfNecessary(request);
664         }
665 
666         @Override
onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result)667         public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
668                 TotalCaptureResult result) {
669             try {
670                 List<CaptureResult> partialResultsList = mPartialResultsMap.get(request);
671                 if (partialResultsList == null) {
672                     Log.w(TAG, "onCaptureCompleted: unknown request");
673                 }
674                 mQueue.put(new Pair<TotalCaptureResult, List<CaptureResult>>(
675                         result, partialResultsList));
676                 mPartialResultsMap.remove(request);
677                 boolean newEntryAdded = completedRequests.add(request);
678                 if (!newEntryAdded) {
679                     Integer frame = (Integer) request.getTag();
680                     Log.e(TAG, "Frame " + frame + "ERROR_DUPLICATED_REQUEST");
681                     errorCode |= ERROR_DUPLICATED_REQUEST;
682                 }
683             } catch (InterruptedException e) {
684                 throw new UnsupportedOperationException(
685                         "Can't handle InterruptedException in onCaptureCompleted");
686             }
687         }
688 
689         @Override
onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult)690         public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
691                 CaptureResult partialResult) {
692             createMapEntryIfNecessary(request);
693             List<CaptureResult> partialResultsList = mPartialResultsMap.get(request);
694             partialResultsList.add(partialResult);
695         }
696 
createMapEntryIfNecessary(CaptureRequest request)697         private void createMapEntryIfNecessary(CaptureRequest request) {
698             if (!mPartialResultsMap.containsKey(request)) {
699                 // create a new entry in the map
700                 mPartialResultsMap.put(request, new ArrayList<CaptureResult>());
701             }
702         }
703 
checkCallbackOrder(CaptureRequest request)704         private void checkCallbackOrder(CaptureRequest request) {
705             if (completedRequests.contains(request)) {
706                 Integer frame = (Integer) request.getTag();
707                 Log.e(TAG, "Frame " + frame + "ERROR_WRONG_CALLBACK_ORDER");
708                 errorCode |= ERROR_WRONG_CALLBACK_ORDER;
709             }
710         }
711 
getCaptureResultPairs(long timeout)712         public Pair<TotalCaptureResult, List<CaptureResult>> getCaptureResultPairs(long timeout) {
713             try {
714                 Pair<TotalCaptureResult, List<CaptureResult>> result =
715                         mQueue.poll(timeout, TimeUnit.MILLISECONDS);
716                 assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result);
717                 return result;
718             } catch (InterruptedException e) {
719                 throw new UnsupportedOperationException("Unhandled interrupted exception", e);
720             }
721         }
722 
getErrorCode()723         public int getErrorCode() {
724             return errorCode;
725         }
726     }
727 
728     /**
729      * TODO: Use CameraCharacteristics.getAvailableCaptureResultKeys() once we can filter out
730      * @hide keys.
731      *
732      */
733 
734     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
735      * The key entries below this point are generated from metadata
736      * definitions in /system/media/camera/docs. Do not modify by hand or
737      * modify the comment blocks at the start or end.
738      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/
739 
getAllCaptureResultKeys()740     private static List<CaptureResult.Key<?>> getAllCaptureResultKeys() {
741         ArrayList<CaptureResult.Key<?>> resultKeys = new ArrayList<CaptureResult.Key<?>>();
742         resultKeys.add(CaptureResult.COLOR_CORRECTION_MODE);
743         resultKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM);
744         resultKeys.add(CaptureResult.COLOR_CORRECTION_GAINS);
745         resultKeys.add(CaptureResult.COLOR_CORRECTION_ABERRATION_MODE);
746         resultKeys.add(CaptureResult.CONTROL_AE_ANTIBANDING_MODE);
747         resultKeys.add(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION);
748         resultKeys.add(CaptureResult.CONTROL_AE_LOCK);
749         resultKeys.add(CaptureResult.CONTROL_AE_MODE);
750         resultKeys.add(CaptureResult.CONTROL_AE_REGIONS);
751         resultKeys.add(CaptureResult.CONTROL_AE_TARGET_FPS_RANGE);
752         resultKeys.add(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
753         resultKeys.add(CaptureResult.CONTROL_AF_MODE);
754         resultKeys.add(CaptureResult.CONTROL_AF_REGIONS);
755         resultKeys.add(CaptureResult.CONTROL_AF_TRIGGER);
756         resultKeys.add(CaptureResult.CONTROL_AWB_LOCK);
757         resultKeys.add(CaptureResult.CONTROL_AWB_MODE);
758         resultKeys.add(CaptureResult.CONTROL_AWB_REGIONS);
759         resultKeys.add(CaptureResult.CONTROL_CAPTURE_INTENT);
760         resultKeys.add(CaptureResult.CONTROL_EFFECT_MODE);
761         resultKeys.add(CaptureResult.CONTROL_MODE);
762         resultKeys.add(CaptureResult.CONTROL_SCENE_MODE);
763         resultKeys.add(CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE);
764         resultKeys.add(CaptureResult.CONTROL_AE_STATE);
765         resultKeys.add(CaptureResult.CONTROL_AF_STATE);
766         resultKeys.add(CaptureResult.CONTROL_AWB_STATE);
767         resultKeys.add(CaptureResult.CONTROL_POST_RAW_SENSITIVITY_BOOST);
768         resultKeys.add(CaptureResult.EDGE_MODE);
769         resultKeys.add(CaptureResult.FLASH_MODE);
770         resultKeys.add(CaptureResult.FLASH_STATE);
771         resultKeys.add(CaptureResult.HOT_PIXEL_MODE);
772         resultKeys.add(CaptureResult.JPEG_GPS_LOCATION);
773         resultKeys.add(CaptureResult.JPEG_ORIENTATION);
774         resultKeys.add(CaptureResult.JPEG_QUALITY);
775         resultKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
776         resultKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
777         resultKeys.add(CaptureResult.LENS_APERTURE);
778         resultKeys.add(CaptureResult.LENS_FILTER_DENSITY);
779         resultKeys.add(CaptureResult.LENS_FOCAL_LENGTH);
780         resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
781         resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
782         resultKeys.add(CaptureResult.LENS_POSE_ROTATION);
783         resultKeys.add(CaptureResult.LENS_POSE_TRANSLATION);
784         resultKeys.add(CaptureResult.LENS_FOCUS_RANGE);
785         resultKeys.add(CaptureResult.LENS_STATE);
786         resultKeys.add(CaptureResult.LENS_INTRINSIC_CALIBRATION);
787         resultKeys.add(CaptureResult.LENS_RADIAL_DISTORTION);
788         resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
789         resultKeys.add(CaptureResult.REQUEST_PIPELINE_DEPTH);
790         resultKeys.add(CaptureResult.SCALER_CROP_REGION);
791         resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
792         resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
793         resultKeys.add(CaptureResult.SENSOR_SENSITIVITY);
794         resultKeys.add(CaptureResult.SENSOR_TIMESTAMP);
795         resultKeys.add(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
796         resultKeys.add(CaptureResult.SENSOR_NOISE_PROFILE);
797         resultKeys.add(CaptureResult.SENSOR_GREEN_SPLIT);
798         resultKeys.add(CaptureResult.SENSOR_TEST_PATTERN_DATA);
799         resultKeys.add(CaptureResult.SENSOR_TEST_PATTERN_MODE);
800         resultKeys.add(CaptureResult.SENSOR_ROLLING_SHUTTER_SKEW);
801         resultKeys.add(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL);
802         resultKeys.add(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL);
803         resultKeys.add(CaptureResult.SHADING_MODE);
804         resultKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE);
805         resultKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE);
806         resultKeys.add(CaptureResult.STATISTICS_FACES);
807         resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
808         resultKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
809         resultKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP);
810         resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP_MODE);
811         resultKeys.add(CaptureResult.TONEMAP_CURVE);
812         resultKeys.add(CaptureResult.TONEMAP_MODE);
813         resultKeys.add(CaptureResult.TONEMAP_GAMMA);
814         resultKeys.add(CaptureResult.TONEMAP_PRESET_CURVE);
815         resultKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
816         resultKeys.add(CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR);
817 
818         return resultKeys;
819     }
820 
821     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
822      * End generated code
823      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
824 }
825