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