• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 org.mockito.ArgumentMatchers.anyBoolean;
20 import static org.mockito.Matchers.any;
21 import static org.mockito.Matchers.anyInt;
22 import static org.mockito.Matchers.anyLong;
23 import static org.mockito.Matchers.eq;
24 import static org.mockito.Mockito.atLeastOnce;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.reset;
27 import static org.mockito.Mockito.timeout;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 
31 import com.android.compatibility.common.util.DeviceReportLog;
32 import com.android.compatibility.common.util.ResultType;
33 import com.android.compatibility.common.util.ResultUnit;
34 import com.android.compatibility.common.util.Stat;
35 import com.android.ex.camera2.blocking.BlockingSessionCallback;
36 import com.android.ex.camera2.blocking.BlockingExtensionSessionCallback;
37 import com.android.ex.camera2.blocking.BlockingStateCallback;
38 import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
39 import com.android.ex.camera2.pos.AutoFocusStateMachine;
40 
41 import android.graphics.ImageFormat;
42 import android.graphics.Rect;
43 import android.graphics.SurfaceTexture;
44 import android.hardware.HardwareBuffer;
45 import android.hardware.camera2.CameraCaptureSession;
46 import android.hardware.camera2.CameraDevice;
47 import android.hardware.camera2.CameraExtensionCharacteristics;
48 import android.hardware.camera2.CameraExtensionSession;
49 import android.hardware.camera2.CameraMetadata;
50 import android.hardware.camera2.CaptureRequest;
51 import android.hardware.camera2.CaptureResult;
52 import android.hardware.camera2.TotalCaptureResult;
53 import android.hardware.camera2.cts.helpers.CameraErrorCollector;
54 import android.hardware.camera2.cts.helpers.StaticMetadata;
55 import android.hardware.camera2.cts.testcases.Camera2AndroidTestRule;
56 import android.hardware.camera2.params.ExtensionSessionConfiguration;
57 import android.hardware.camera2.params.MeteringRectangle;
58 import android.hardware.camera2.params.OutputConfiguration;
59 import android.hardware.camera2.params.SessionConfiguration;
60 import android.media.ExifInterface;
61 import android.media.Image;
62 import android.media.ImageReader;
63 import android.os.SystemClock;
64 import android.util.Range;
65 import android.util.Size;
66 
67 import static android.hardware.camera2.cts.CameraTestUtils.*;
68 import static android.hardware.cts.helpers.CameraUtils.*;
69 
70 import android.util.Log;
71 import android.view.Surface;
72 import android.view.TextureView;
73 
74 import androidx.test.platform.app.InstrumentationRegistry;
75 import androidx.test.rule.ActivityTestRule;
76 
77 import org.junit.Rule;
78 import org.junit.Test;
79 import org.junit.runner.RunWith;
80 import org.junit.runners.Parameterized;
81 
82 import java.io.IOException;
83 import java.util.ArrayList;
84 import java.util.Arrays;
85 import java.util.HashSet;
86 import java.util.List;
87 import java.util.Set;
88 import java.util.stream.Collectors;
89 
90 @RunWith(Parameterized.class)
91 public class CameraExtensionSessionTest extends Camera2ParameterizedTestCase {
92     private static final String TAG = "CameraExtensionSessionTest";
93     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
94     private static final long WAIT_FOR_COMMAND_TO_COMPLETE_MS = 5000;
95     private static final long REPEATING_REQUEST_TIMEOUT_MS = 5000;
96     private static final int MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS = 10000;
97     private static final float ZOOM_ERROR_MARGIN = 0.05f;
98 
99     private SurfaceTexture mSurfaceTexture = null;
100     private Camera2AndroidTestRule mTestRule = null;
101     private CameraErrorCollector mCollector =  null;
102 
103     private DeviceReportLog mReportLog;
104 
105     @Override
setUp()106     public void setUp() throws Exception {
107         super.setUp();
108         mTestRule = new Camera2AndroidTestRule(mContext);
109         mTestRule.before();
110         mCollector = new CameraErrorCollector();
111     }
112 
113     @Override
tearDown()114     public void tearDown() throws Exception {
115         if (mTestRule != null) {
116             mTestRule.after();
117         }
118         if (mSurfaceTexture != null) {
119             mSurfaceTexture.release();
120             mSurfaceTexture = null;
121         }
122         if (mCollector != null) {
123             try {
124                 mCollector.verify();
125             } catch (Throwable e) {
126                 throw new Exception(e.getMessage());
127             }
128         }
129         super.tearDown();
130     }
131 
132     @Rule
133     public ActivityTestRule<CameraExtensionTestActivity> mActivityRule =
134             new ActivityTestRule<>(CameraExtensionTestActivity.class);
135 
updatePreviewSurfaceTexture()136     private void updatePreviewSurfaceTexture() {
137         if (mSurfaceTexture != null) {
138             return;
139         }
140 
141         TextureView textureView = mActivityRule.getActivity().getTextureView();
142         mSurfaceTexture = getAvailableSurfaceTexture(WAIT_FOR_COMMAND_TO_COMPLETE_MS, textureView);
143         assertNotNull("Failed to acquire valid preview surface texture!", mSurfaceTexture);
144     }
145 
146     // Verify that camera extension sessions can be created and closed as expected.
147     @Test
testBasicExtensionLifecycle()148     public void testBasicExtensionLifecycle() throws Exception {
149         for (String id : mCameraIdsUnderTest) {
150             StaticMetadata staticMeta =
151                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
152             if (!staticMeta.isColorOutputSupported()) {
153                 continue;
154             }
155             updatePreviewSurfaceTexture();
156             CameraExtensionCharacteristics extensionChars =
157                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
158             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
159             for (Integer extension : supportedExtensions) {
160                 List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
161                         mSurfaceTexture.getClass());
162                 Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
163                 mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
164                 OutputConfiguration outputConfig = new OutputConfiguration(
165                         new Surface(mSurfaceTexture));
166                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
167                 outputConfigs.add(outputConfig);
168 
169                 BlockingExtensionSessionCallback sessionListener =
170                         new BlockingExtensionSessionCallback(
171                                 mock(CameraExtensionSession.StateCallback.class));
172                 ExtensionSessionConfiguration configuration =
173                         new ExtensionSessionConfiguration(extension, outputConfigs,
174                                 new HandlerExecutor(mTestRule.getHandler()), sessionListener);
175 
176                 try {
177                     mTestRule.openDevice(id);
178                     CameraDevice camera = mTestRule.getCamera();
179                     camera.createExtensionSession(configuration);
180                     CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
181                             SESSION_CONFIGURE_TIMEOUT_MS);
182 
183                     extensionSession.close();
184                     sessionListener.getStateWaiter().waitForState(
185                             BlockingExtensionSessionCallback.SESSION_CLOSED,
186                             SESSION_CLOSE_TIMEOUT_MS);
187                 } finally {
188                     mTestRule.closeDevice(id);
189                 }
190             }
191         }
192     }
193 
194     // Verify that regular camera sessions close as expected after creating a camera extension
195     // session.
196     @Test
testCloseCaptureSession()197     public void testCloseCaptureSession() throws Exception {
198         for (String id : mCameraIdsUnderTest) {
199             StaticMetadata staticMeta =
200                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
201             if (!staticMeta.isColorOutputSupported()) {
202                 continue;
203             }
204             updatePreviewSurfaceTexture();
205             CameraExtensionCharacteristics extensionChars =
206                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
207             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
208             for (Integer extension : supportedExtensions) {
209                 List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
210                         mSurfaceTexture.getClass());
211                 Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
212                 mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
213                 Surface repeatingSurface = new Surface(mSurfaceTexture);
214                 OutputConfiguration textureOutput = new OutputConfiguration(repeatingSurface);
215                 List<OutputConfiguration> outputs = new ArrayList<>();
216                 outputs.add(textureOutput);
217                 BlockingSessionCallback regularSessionListener = new BlockingSessionCallback(
218                         mock(CameraCaptureSession.StateCallback.class));
219                 SessionConfiguration regularConfiguration = new SessionConfiguration(
220                         SessionConfiguration.SESSION_REGULAR, outputs,
221                         new HandlerExecutor(mTestRule.getHandler()), regularSessionListener);
222 
223                 BlockingExtensionSessionCallback sessionListener =
224                         new BlockingExtensionSessionCallback(mock(
225                                 CameraExtensionSession.StateCallback.class));
226                 ExtensionSessionConfiguration configuration =
227                         new ExtensionSessionConfiguration(extension, outputs,
228                                 new HandlerExecutor(mTestRule.getHandler()), sessionListener);
229 
230                 try {
231                     mTestRule.openDevice(id);
232                     mTestRule.getCamera().createCaptureSession(regularConfiguration);
233 
234                     CameraCaptureSession session =
235                             regularSessionListener
236                                     .waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
237                     assertNotNull(session);
238 
239                     CameraDevice camera = mTestRule.getCamera();
240                     camera.createExtensionSession(configuration);
241                     CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
242                             SESSION_CONFIGURE_TIMEOUT_MS);
243                     assertNotNull(extensionSession);
244 
245                     regularSessionListener.getStateWaiter().waitForState(
246                             BlockingExtensionSessionCallback.SESSION_CLOSED,
247                             SESSION_CLOSE_TIMEOUT_MS);
248 
249                     extensionSession.close();
250                     sessionListener.getStateWaiter().waitForState(
251                             BlockingExtensionSessionCallback.SESSION_CLOSED,
252                             SESSION_CLOSE_TIMEOUT_MS);
253                 } finally {
254                     mTestRule.closeDevice(id);
255                 }
256             }
257         }
258     }
259 
260     // Verify that camera extension sessions close as expected when creating a regular capture
261     // session.
262     @Test
testCloseExtensionSession()263     public void testCloseExtensionSession() throws Exception {
264         for (String id : mCameraIdsUnderTest) {
265             StaticMetadata staticMeta =
266                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
267             if (!staticMeta.isColorOutputSupported()) {
268                 continue;
269             }
270             updatePreviewSurfaceTexture();
271             CameraExtensionCharacteristics extensionChars =
272                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
273             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
274             for (Integer extension : supportedExtensions) {
275                 List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
276                         mSurfaceTexture.getClass());
277                 Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
278                 mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
279                 Surface surface = new Surface(mSurfaceTexture);
280                 OutputConfiguration textureOutput = new OutputConfiguration(surface);
281                 List<OutputConfiguration> outputs = new ArrayList<>();
282                 outputs.add(textureOutput);
283                 BlockingSessionCallback regularSessionListener = new BlockingSessionCallback(
284                         mock(CameraCaptureSession.StateCallback.class));
285                 SessionConfiguration regularConfiguration = new SessionConfiguration(
286                         SessionConfiguration.SESSION_REGULAR, outputs,
287                         new HandlerExecutor(mTestRule.getHandler()), regularSessionListener);
288 
289                 BlockingExtensionSessionCallback sessionListener =
290                         new BlockingExtensionSessionCallback(mock(
291                                 CameraExtensionSession.StateCallback.class));
292                 ExtensionSessionConfiguration configuration =
293                         new ExtensionSessionConfiguration(extension, outputs,
294                                 new HandlerExecutor(mTestRule.getHandler()), sessionListener);
295 
296                 try {
297                     mTestRule.openDevice(id);
298                     CameraDevice camera = mTestRule.getCamera();
299                     camera.createExtensionSession(configuration);
300                     CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
301                             SESSION_CONFIGURE_TIMEOUT_MS);
302                     assertNotNull(extensionSession);
303 
304                     mTestRule.getCamera().createCaptureSession(regularConfiguration);
305                     sessionListener.getStateWaiter().waitForState(
306                             BlockingExtensionSessionCallback.SESSION_CLOSED,
307                             SESSION_CLOSE_TIMEOUT_MS);
308 
309                     CameraCaptureSession session =
310                             regularSessionListener.waitAndGetSession(
311                                     SESSION_CONFIGURE_TIMEOUT_MS);
312                     session.close();
313                     regularSessionListener.getStateWaiter().waitForState(
314                             BlockingSessionCallback.SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
315                 } finally {
316                     mTestRule.closeDevice(id);
317                 }
318             }
319         }
320     }
321 
322     // Verify camera device query
323     @Test
testGetDevice()324     public void testGetDevice() throws Exception {
325         for (String id : mCameraIdsUnderTest) {
326             StaticMetadata staticMeta =
327                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
328             if (!staticMeta.isColorOutputSupported()) {
329                 continue;
330             }
331             updatePreviewSurfaceTexture();
332             CameraExtensionCharacteristics extensionChars =
333                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
334             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
335             for (Integer extension : supportedExtensions) {
336                 List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
337                         mSurfaceTexture.getClass());
338                 Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
339                 mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
340                 OutputConfiguration privateOutput = new OutputConfiguration(
341                         new Surface(mSurfaceTexture));
342                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
343                 outputConfigs.add(privateOutput);
344 
345                 BlockingExtensionSessionCallback sessionListener =
346                         new BlockingExtensionSessionCallback(
347                                 mock(CameraExtensionSession.StateCallback.class));
348                 ExtensionSessionConfiguration configuration =
349                         new ExtensionSessionConfiguration(extension, outputConfigs,
350                                 new HandlerExecutor(mTestRule.getHandler()), sessionListener);
351 
352                 try {
353                     mTestRule.openDevice(id);
354                     CameraDevice camera = mTestRule.getCamera();
355                     camera.createExtensionSession(configuration);
356                     CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
357                             SESSION_CONFIGURE_TIMEOUT_MS);
358 
359                     assertEquals("Unexpected/Invalid camera device", mTestRule.getCamera(),
360                             extensionSession.getDevice());
361                 } finally {
362                     mTestRule.closeDevice(id);
363                 }
364 
365                 try {
366                     sessionListener.getStateWaiter().waitForState(
367                             BlockingExtensionSessionCallback.SESSION_CLOSED,
368                             SESSION_CLOSE_TIMEOUT_MS);
369                     fail("should get TimeoutRuntimeException due to previously closed camera "
370                             + "device");
371                 } catch (TimeoutRuntimeException e) {
372                     // Expected, per API spec we should not receive any further session callbacks
373                     // besides the device state 'onClosed' callback.
374                 }
375             }
376         }
377     }
378 
379     // Test case for repeating/stopRepeating on all supported extensions and expected state/capture
380     // callbacks.
381     @Test
testRepeatingCapture()382     public void testRepeatingCapture() throws Exception {
383         for (String id : mCameraIdsUnderTest) {
384             StaticMetadata staticMeta =
385                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
386             if (!staticMeta.isColorOutputSupported()) {
387                 continue;
388             }
389             updatePreviewSurfaceTexture();
390             CameraExtensionCharacteristics extensionChars =
391                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
392             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
393             for (Integer extension : supportedExtensions) {
394                 List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
395                         mSurfaceTexture.getClass());
396                 Size maxSize =
397                         CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
398                 mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(),
399                         maxSize.getHeight());
400                 Surface texturedSurface = new Surface(mSurfaceTexture);
401 
402                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
403                 outputConfigs.add(new OutputConfiguration(texturedSurface));
404 
405                 BlockingExtensionSessionCallback sessionListener =
406                         new BlockingExtensionSessionCallback(mock(
407                                 CameraExtensionSession.StateCallback.class));
408                 ExtensionSessionConfiguration configuration =
409                         new ExtensionSessionConfiguration(extension, outputConfigs,
410                                 new HandlerExecutor(mTestRule.getHandler()),
411                                 sessionListener);
412 
413                 boolean captureResultsSupported =
414                         !extensionChars.getAvailableCaptureResultKeys(extension).isEmpty();
415 
416                 try {
417                     mTestRule.openDevice(id);
418                     CameraDevice camera = mTestRule.getCamera();
419                     camera.createExtensionSession(configuration);
420                     CameraExtensionSession extensionSession =
421                             sessionListener.waitAndGetSession(
422                                     SESSION_CONFIGURE_TIMEOUT_MS);
423                     assertNotNull(extensionSession);
424 
425                     CaptureRequest.Builder captureBuilder =
426                             mTestRule.getCamera().createCaptureRequest(
427                                     android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
428                     captureBuilder.addTarget(texturedSurface);
429                     CameraExtensionSession.ExtensionCaptureCallback captureCallbackMock =
430                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
431                     SimpleCaptureCallback simpleCaptureCallback =
432                             new SimpleCaptureCallback(captureCallbackMock,
433                                     extensionChars.getAvailableCaptureResultKeys(extension),
434                                     mCollector);
435                     CaptureRequest request = captureBuilder.build();
436                     int sequenceId = extensionSession.setRepeatingRequest(request,
437                             new HandlerExecutor(mTestRule.getHandler()), simpleCaptureCallback);
438 
439                     verify(captureCallbackMock,
440                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
441                             .onCaptureStarted(eq(extensionSession), eq(request), anyLong());
442                     verify(captureCallbackMock,
443                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
444                             .onCaptureProcessStarted(extensionSession, request);
445                     if (captureResultsSupported) {
446                         verify(captureCallbackMock,
447                                 timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).atLeastOnce())
448                                 .onCaptureResultAvailable(eq(extensionSession), eq(request),
449                                         any(TotalCaptureResult.class));
450                     }
451 
452                     extensionSession.stopRepeating();
453 
454                     verify(captureCallbackMock,
455                             timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
456                             .onCaptureSequenceCompleted(extensionSession, sequenceId);
457 
458                     verify(captureCallbackMock, times(0))
459                             .onCaptureSequenceAborted(any(CameraExtensionSession.class),
460                                     anyInt());
461 
462                     extensionSession.close();
463 
464                     sessionListener.getStateWaiter().waitForState(
465                             BlockingExtensionSessionCallback.SESSION_CLOSED,
466                             SESSION_CLOSE_TIMEOUT_MS);
467 
468                     assertTrue("The sum of onCaptureProcessStarted and onCaptureFailed" +
469                                     " callbacks must be greater or equal than the number of calls" +
470                                     " to onCaptureStarted!",
471                             simpleCaptureCallback.getTotalFramesArrived() +
472                                     simpleCaptureCallback.getTotalFramesFailed() >=
473                                     simpleCaptureCallback.getTotalFramesStarted());
474                     assertTrue(String.format("The last repeating request surface timestamp " +
475                                     "%d must be less than or equal to the last " +
476                                     "onCaptureStarted " +
477                                     "timestamp %d", mSurfaceTexture.getTimestamp(),
478                             simpleCaptureCallback.getLastTimestamp()),
479                             mSurfaceTexture.getTimestamp() <=
480                                     simpleCaptureCallback.getLastTimestamp());
481                 } finally {
482                     mTestRule.closeDevice(id);
483                     texturedSurface.release();
484                 }
485             }
486         }
487     }
488 
489     // Test case for multi-frame only capture on all supported extensions and expected state
490     // callbacks. Verify still frame output, measure the average capture latency and if possible
491     // ensure that the value is within the reported range.
492     @Test
testMultiFrameCapture()493     public void testMultiFrameCapture() throws Exception {
494         final int IMAGE_COUNT = 10;
495         final int SUPPORTED_CAPTURE_OUTPUT_FORMATS[] = {
496                 ImageFormat.YUV_420_888,
497                 ImageFormat.JPEG
498         };
499         for (String id : mCameraIdsUnderTest) {
500             StaticMetadata staticMeta =
501                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
502             if (!staticMeta.isColorOutputSupported()) {
503                 continue;
504             }
505             updatePreviewSurfaceTexture();
506             CameraExtensionCharacteristics extensionChars =
507                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
508             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
509             for (Integer extension : supportedExtensions) {
510                 for (int captureFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
511                     List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
512                             captureFormat);
513                     if (extensionSizes.isEmpty()) {
514                         continue;
515                     }
516                     Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
517                     SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false,
518                             1);
519                     ImageReader extensionImageReader = CameraTestUtils.makeImageReader(maxSize,
520                             captureFormat, /*maxImages*/ 1, imageListener,
521                             mTestRule.getHandler());
522                     Surface imageReaderSurface = extensionImageReader.getSurface();
523                     OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
524                     List<OutputConfiguration> outputConfigs = new ArrayList<>();
525                     outputConfigs.add(readerOutput);
526 
527                     BlockingExtensionSessionCallback sessionListener =
528                             new BlockingExtensionSessionCallback(mock(
529                                     CameraExtensionSession.StateCallback.class));
530                     ExtensionSessionConfiguration configuration =
531                             new ExtensionSessionConfiguration(extension, outputConfigs,
532                                     new HandlerExecutor(mTestRule.getHandler()),
533                                     sessionListener);
534                     String streamName = "test_extension_capture";
535                     mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
536                     mReportLog.addValue("camera_id", id, ResultType.NEUTRAL, ResultUnit.NONE);
537                     mReportLog.addValue("extension_id", extension, ResultType.NEUTRAL,
538                             ResultUnit.NONE);
539                     double[] captureTimes = new double[IMAGE_COUNT];
540                     boolean captureResultsSupported =
541                             !extensionChars.getAvailableCaptureResultKeys(extension).isEmpty();
542 
543                     try {
544                         mTestRule.openDevice(id);
545                         CameraDevice camera = mTestRule.getCamera();
546                         camera.createExtensionSession(configuration);
547                         CameraExtensionSession extensionSession =
548                                 sessionListener.waitAndGetSession(
549                                         SESSION_CONFIGURE_TIMEOUT_MS);
550                         assertNotNull(extensionSession);
551 
552                         CaptureRequest.Builder captureBuilder =
553                                 mTestRule.getCamera().createCaptureRequest(
554                                         CameraDevice.TEMPLATE_STILL_CAPTURE);
555                         captureBuilder.addTarget(imageReaderSurface);
556                         CameraExtensionSession.ExtensionCaptureCallback captureMockCallback =
557                                 mock(CameraExtensionSession.ExtensionCaptureCallback.class);
558                         SimpleCaptureCallback captureCallback =
559                                 new SimpleCaptureCallback(captureMockCallback,
560                                         extensionChars.getAvailableCaptureResultKeys(extension),
561                                         mCollector);
562 
563                         for (int i = 0; i < IMAGE_COUNT; i++) {
564                             int jpegOrientation = (i * 90) % 360; // degrees [0..270]
565                             if (captureFormat == ImageFormat.JPEG) {
566                                 captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
567                                         jpegOrientation);
568                             }
569                             CaptureRequest request = captureBuilder.build();
570                             long startTimeMs = SystemClock.elapsedRealtime();
571                             int sequenceId = extensionSession.capture(request,
572                                     new HandlerExecutor(mTestRule.getHandler()), captureCallback);
573 
574                             Image img =
575                                     imageListener.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
576                             captureTimes[i] = SystemClock.elapsedRealtime() - startTimeMs;
577                             if (captureFormat == ImageFormat.JPEG) {
578                                 verifyJpegOrientation(img, maxSize, jpegOrientation);
579                             } else {
580                                 validateImage(img, maxSize.getWidth(), maxSize.getHeight(),
581                                         captureFormat, null);
582                             }
583                             Long imgTs = img.getTimestamp();
584                             img.close();
585 
586                             verify(captureMockCallback,
587                                     timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
588                                     .onCaptureStarted(eq(extensionSession), eq(request), eq(imgTs));
589                             verify(captureMockCallback, times(1))
590                                     .onCaptureStarted(eq(extensionSession), eq(request), anyLong());
591                             verify(captureMockCallback,
592                                     timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
593                                     .onCaptureProcessStarted(extensionSession, request);
594                             verify(captureMockCallback,
595                                     timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
596                                     .onCaptureSequenceCompleted(extensionSession, sequenceId);
597                             if (captureResultsSupported) {
598                                 verify(captureMockCallback,
599                                         timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
600                                         .onCaptureResultAvailable(eq(extensionSession), eq(request),
601                                                 any(TotalCaptureResult.class));
602                             }
603                         }
604 
605                         mReportLog.addValue("width", maxSize.getWidth(), ResultType.NEUTRAL,
606                                 ResultUnit.NONE);
607                         mReportLog.addValue("height", maxSize.getHeight(),
608                                 ResultType.NEUTRAL, ResultUnit.NONE);
609                         mReportLog.addValue("format", captureFormat, ResultType.NEUTRAL,
610                                 ResultUnit.NONE);
611                         long avgCaptureLatency = (long) Stat.getAverage(captureTimes);
612                         mReportLog.addValue("avg_latency", avgCaptureLatency,
613                                 ResultType.LOWER_BETTER, ResultUnit.MS);
614 
615                         verify(captureMockCallback, times(0))
616                                 .onCaptureSequenceAborted(any(CameraExtensionSession.class),
617                                         anyInt());
618                         verify(captureMockCallback, times(0))
619                                 .onCaptureFailed(any(CameraExtensionSession.class),
620                                         any(CaptureRequest.class));
621                         Range<Long> latencyRange =
622                                 extensionChars.getEstimatedCaptureLatencyRangeMillis(extension,
623                                         maxSize, captureFormat);
624                         if (latencyRange != null) {
625                             String msg = String.format("Camera [%s]: The measured average "
626                                             + "capture latency of %d ms. for extension type %d  "
627                                             + "with image format: %d and size: %dx%d must be "
628                                             + "within the advertised range of [%d, %d] ms.",
629                                     id, avgCaptureLatency, extension, captureFormat,
630                                     maxSize.getWidth(), maxSize.getHeight(),
631                                     latencyRange.getLower(), latencyRange.getUpper());
632                             assertTrue(msg, latencyRange.contains(avgCaptureLatency));
633                         }
634 
635                         extensionSession.close();
636 
637                         sessionListener.getStateWaiter().waitForState(
638                                 BlockingExtensionSessionCallback.SESSION_CLOSED,
639                                 SESSION_CLOSE_TIMEOUT_MS);
640                     } finally {
641                         mTestRule.closeDevice(id);
642                         extensionImageReader.close();
643                         mReportLog.submit(InstrumentationRegistry.getInstrumentation());
644                     }
645                 }
646             }
647         }
648     }
649 
650     // Verify concurrent extension sessions behavior
651     @Test
testConcurrentSessions()652     public void testConcurrentSessions() throws Exception {
653         Set<Set<String>> concurrentCameraIdSet =
654                 mTestRule.getCameraManager().getConcurrentCameraIds();
655         if (concurrentCameraIdSet.isEmpty()) {
656             return;
657         }
658 
659         for (String id : mCameraIdsUnderTest) {
660             StaticMetadata staticMeta =
661                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
662             if (!staticMeta.isColorOutputSupported()) {
663                 continue;
664             }
665             CameraExtensionCharacteristics extensionChars =
666                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
667             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
668             if (supportedExtensions.isEmpty()) {
669                 continue;
670             }
671 
672             Set<String> concurrentCameraIds = null;
673             for (Set<String> entry : concurrentCameraIdSet) {
674                 if (entry.contains(id)) {
675                     concurrentCameraIds = entry;
676                     break;
677                 }
678             }
679             if (concurrentCameraIds == null) {
680                 continue;
681             }
682 
683             String concurrentCameraId = null;
684             CameraExtensionCharacteristics concurrentExtensionChars = null;
685             for (String entry : concurrentCameraIds) {
686                 if (entry.equals(id)) {
687                     continue;
688                 }
689                 if (!(new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(
690                         entry))).isColorOutputSupported()) {
691                     continue;
692                 }
693                 CameraExtensionCharacteristics chars =
694                         mTestRule.getCameraManager().getCameraExtensionCharacteristics(entry);
695                 if (chars.getSupportedExtensions().isEmpty()) {
696                     continue;
697                 }
698                 concurrentCameraId = entry;
699                 concurrentExtensionChars = chars;
700                 break;
701             }
702             if ((concurrentCameraId == null) || (concurrentExtensionChars == null)) {
703                 continue;
704             }
705 
706             updatePreviewSurfaceTexture();
707             int extensionId = supportedExtensions.get(0);
708             List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extensionId,
709                     mSurfaceTexture.getClass());
710             Size maxSize = CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
711             mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(), maxSize.getHeight());
712             OutputConfiguration outputConfig = new OutputConfiguration(
713                     new Surface(mSurfaceTexture));
714             List<OutputConfiguration> outputConfigs = new ArrayList<>();
715             outputConfigs.add(outputConfig);
716 
717             BlockingExtensionSessionCallback sessionListener =
718                     new BlockingExtensionSessionCallback(
719                             mock(CameraExtensionSession.StateCallback.class));
720             ExtensionSessionConfiguration configuration =
721                     new ExtensionSessionConfiguration(extensionId, outputConfigs,
722                             new HandlerExecutor(mTestRule.getHandler()), sessionListener);
723 
724             CameraDevice concurrentCameraDevice = null;
725             ImageReader extensionImageReader = null;
726             try {
727                 mTestRule.openDevice(id);
728                 CameraDevice camera = mTestRule.getCamera();
729                 camera.createExtensionSession(configuration);
730                 CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
731                         SESSION_CONFIGURE_TIMEOUT_MS);
732                 assertNotNull(extensionSession);
733 
734                 concurrentCameraDevice = CameraTestUtils.openCamera(mTestRule.getCameraManager(),
735                         concurrentCameraId, new BlockingStateCallback(), mTestRule.getHandler());
736                 assertNotNull(concurrentCameraDevice);
737                 int concurrentExtensionId =
738                         concurrentExtensionChars.getSupportedExtensions().get(0);
739                 List<Size> captureSizes = concurrentExtensionChars.getExtensionSupportedSizes(
740                         concurrentExtensionId, mSurfaceTexture.getClass());
741                 assertFalse("No SurfaceTexture output supported", captureSizes.isEmpty());
742                 Size captureMaxSize =
743                         CameraTestUtils.getMaxSize(captureSizes.toArray(new Size[0]));
744 
745                 extensionImageReader = ImageReader.newInstance(
746                         captureMaxSize.getWidth(), captureMaxSize.getHeight(), ImageFormat.PRIVATE,
747                         /*maxImages*/ 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
748                 Surface imageReaderSurface = extensionImageReader.getSurface();
749                 OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
750                 outputConfigs = new ArrayList<>();
751                 outputConfigs.add(readerOutput);
752                 CameraExtensionSession.StateCallback mockSessionListener =
753                         mock(CameraExtensionSession.StateCallback.class);
754                 ExtensionSessionConfiguration concurrentConfiguration =
755                         new ExtensionSessionConfiguration(concurrentExtensionId, outputConfigs,
756                                 new HandlerExecutor(mTestRule.getHandler()),
757                                 mockSessionListener);
758                 concurrentCameraDevice.createExtensionSession(concurrentConfiguration);
759                 // Trying to initialize multiple concurrent extension sessions is expected to fail
760                 verify(mockSessionListener, timeout(SESSION_CONFIGURE_TIMEOUT_MS).times(1))
761                         .onConfigureFailed(any(CameraExtensionSession.class));
762                 verify(mockSessionListener, times(0)).onConfigured(
763                         any(CameraExtensionSession.class));
764 
765                 extensionSession.close();
766                 sessionListener.getStateWaiter().waitForState(
767                         BlockingExtensionSessionCallback.SESSION_CLOSED,
768                         SESSION_CLOSE_TIMEOUT_MS);
769 
770                 // Initialization of another extension session must now be possible
771                 BlockingExtensionSessionCallback concurrentSessionListener =
772                         new BlockingExtensionSessionCallback(
773                                 mock(CameraExtensionSession.StateCallback.class));
774                 concurrentConfiguration = new ExtensionSessionConfiguration(concurrentExtensionId,
775                         outputConfigs, new HandlerExecutor(mTestRule.getHandler()),
776                         concurrentSessionListener);
777                 concurrentCameraDevice.createExtensionSession(concurrentConfiguration);
778                 extensionSession = concurrentSessionListener.waitAndGetSession(
779                         SESSION_CONFIGURE_TIMEOUT_MS);
780                 assertNotNull(extensionSession);
781                 extensionSession.close();
782                 concurrentSessionListener.getStateWaiter().waitForState(
783                         BlockingExtensionSessionCallback.SESSION_CLOSED,
784                         SESSION_CLOSE_TIMEOUT_MS);
785             } finally {
786                 mTestRule.closeDevice(id);
787                 if (concurrentCameraDevice != null) {
788                     concurrentCameraDevice.close();
789                 }
790                 if (extensionImageReader != null) {
791                     extensionImageReader.close();
792                 }
793             }
794         }
795     }
796 
797     // Test case combined repeating with multi frame capture on all supported extensions.
798     // Verify still frame output.
799     @Test
testRepeatingAndCaptureCombined()800     public void testRepeatingAndCaptureCombined() throws Exception {
801         for (String id : mCameraIdsUnderTest) {
802             StaticMetadata staticMeta =
803                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
804             if (!staticMeta.isColorOutputSupported()) {
805                 continue;
806             }
807             updatePreviewSurfaceTexture();
808             CameraExtensionCharacteristics extensionChars =
809                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
810             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
811             for (Integer extension : supportedExtensions) {
812                 int captureFormat = ImageFormat.JPEG;
813                 List<Size> captureSizes = extensionChars.getExtensionSupportedSizes(extension,
814                         captureFormat);
815                 assertFalse("No Jpeg output supported", captureSizes.isEmpty());
816                 Size captureMaxSize =
817                         CameraTestUtils.getMaxSize(captureSizes.toArray(new Size[0]));
818 
819                 SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false
820                         , 1);
821                 ImageReader extensionImageReader = CameraTestUtils.makeImageReader(
822                         captureMaxSize, captureFormat, /*maxImages*/ 1, imageListener,
823                         mTestRule.getHandler());
824                 Surface imageReaderSurface = extensionImageReader.getSurface();
825                 OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
826                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
827                 outputConfigs.add(readerOutput);
828 
829                 // Pick a supported preview/repeating size with aspect ratio close to the
830                 // multi-frame capture size
831                 List<Size> repeatingSizes = extensionChars.getExtensionSupportedSizes(extension,
832                         mSurfaceTexture.getClass());
833                 Size maxRepeatingSize =
834                         CameraTestUtils.getMaxSize(repeatingSizes.toArray(new Size[0]));
835                 List<Size> previewSizes = getSupportedPreviewSizes(id,
836                         mTestRule.getCameraManager(),
837                         getPreviewSizeBound(mTestRule.getWindowManager(), PREVIEW_SIZE_BOUND));
838                 List<Size> supportedPreviewSizes =
839                         previewSizes.stream().filter(repeatingSizes::contains).collect(
840                                 Collectors.toList());
841                 if (!supportedPreviewSizes.isEmpty()) {
842                     float targetAr =
843                             ((float) captureMaxSize.getWidth()) / captureMaxSize.getHeight();
844                     for (Size s : supportedPreviewSizes) {
845                         float currentAr = ((float) s.getWidth()) / s.getHeight();
846                         if (Math.abs(targetAr - currentAr) < 0.01) {
847                             maxRepeatingSize = s;
848                             break;
849                         }
850                     }
851                 }
852 
853                 boolean captureResultsSupported =
854                         !extensionChars.getAvailableCaptureResultKeys(extension).isEmpty();
855 
856                 mSurfaceTexture.setDefaultBufferSize(maxRepeatingSize.getWidth(),
857                         maxRepeatingSize.getHeight());
858                 Surface texturedSurface = new Surface(mSurfaceTexture);
859                 outputConfigs.add(new OutputConfiguration(texturedSurface));
860 
861                 BlockingExtensionSessionCallback sessionListener =
862                         new BlockingExtensionSessionCallback(mock(
863                                 CameraExtensionSession.StateCallback.class));
864                 ExtensionSessionConfiguration configuration =
865                         new ExtensionSessionConfiguration(extension, outputConfigs,
866                                 new HandlerExecutor(mTestRule.getHandler()),
867                                 sessionListener);
868                 try {
869                     mTestRule.openDevice(id);
870                     CameraDevice camera = mTestRule.getCamera();
871                     camera.createExtensionSession(configuration);
872                     CameraExtensionSession extensionSession =
873                             sessionListener.waitAndGetSession(
874                                     SESSION_CONFIGURE_TIMEOUT_MS);
875                     assertNotNull(extensionSession);
876 
877                     CaptureRequest.Builder captureBuilder =
878                             mTestRule.getCamera().createCaptureRequest(
879                                     android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
880                     captureBuilder.addTarget(texturedSurface);
881                     CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
882                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
883                     SimpleCaptureCallback repeatingCaptureCallback =
884                             new SimpleCaptureCallback(repeatingCallbackMock,
885                                     extensionChars.getAvailableCaptureResultKeys(extension),
886                                     mCollector);
887                     CaptureRequest repeatingRequest = captureBuilder.build();
888                     int repeatingSequenceId =
889                             extensionSession.setRepeatingRequest(repeatingRequest,
890                                     new HandlerExecutor(mTestRule.getHandler()),
891                                     repeatingCaptureCallback);
892 
893                     Thread.sleep(REPEATING_REQUEST_TIMEOUT_MS);
894 
895                     verify(repeatingCallbackMock, atLeastOnce())
896                             .onCaptureStarted(eq(extensionSession), eq(repeatingRequest),
897                                     anyLong());
898                     verify(repeatingCallbackMock, atLeastOnce())
899                             .onCaptureProcessStarted(extensionSession, repeatingRequest);
900                     if (captureResultsSupported) {
901                         verify(repeatingCallbackMock,
902                                 timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).atLeastOnce())
903                                 .onCaptureResultAvailable(eq(extensionSession),
904                                         eq(repeatingRequest), any(TotalCaptureResult.class));
905                     }
906 
907                     captureBuilder = mTestRule.getCamera().createCaptureRequest(
908                             android.hardware.camera2.CameraDevice.TEMPLATE_STILL_CAPTURE);
909                     captureBuilder.addTarget(imageReaderSurface);
910                     CameraExtensionSession.ExtensionCaptureCallback captureCallback =
911                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
912 
913                     CaptureRequest captureRequest = captureBuilder.build();
914                     int captureSequenceId = extensionSession.capture(captureRequest,
915                             new HandlerExecutor(mTestRule.getHandler()), captureCallback);
916 
917                     Image img =
918                             imageListener.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
919                     validateImage(img, captureMaxSize.getWidth(),
920                             captureMaxSize.getHeight(), captureFormat, null);
921                     Long imgTs = img.getTimestamp();
922                     img.close();
923 
924                     verify(captureCallback, times(1))
925                             .onCaptureStarted(eq(extensionSession), eq(captureRequest), eq(imgTs));
926                     verify(captureCallback, timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
927                             .onCaptureProcessStarted(extensionSession, captureRequest);
928                     if (captureResultsSupported) {
929                         verify(captureCallback,
930                                 timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
931                                 .onCaptureResultAvailable(eq(extensionSession),
932                                         eq(captureRequest), any(TotalCaptureResult.class));
933                     }
934                     verify(captureCallback, timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
935                             .onCaptureSequenceCompleted(extensionSession,
936                                     captureSequenceId);
937                     verify(captureCallback, times(0))
938                             .onCaptureSequenceAborted(any(CameraExtensionSession.class),
939                                     anyInt());
940                     verify(captureCallback, times(0))
941                             .onCaptureFailed(any(CameraExtensionSession.class),
942                                     any(CaptureRequest.class));
943 
944                     extensionSession.stopRepeating();
945 
946                     verify(repeatingCallbackMock,
947                             timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
948                             .onCaptureSequenceCompleted(extensionSession, repeatingSequenceId);
949 
950                     verify(repeatingCallbackMock, times(0))
951                             .onCaptureSequenceAborted(any(CameraExtensionSession.class),
952                                     anyInt());
953 
954                     extensionSession.close();
955 
956                     sessionListener.getStateWaiter().waitForState(
957                             BlockingExtensionSessionCallback.SESSION_CLOSED,
958                             SESSION_CLOSE_TIMEOUT_MS);
959 
960                     assertTrue("The sum of onCaptureProcessStarted and onCaptureFailed" +
961                                     " callbacks must be greater or equal than the number of calls" +
962                                     " to onCaptureStarted!",
963                             repeatingCaptureCallback.getTotalFramesArrived() +
964                                     repeatingCaptureCallback.getTotalFramesFailed() >=
965                             repeatingCaptureCallback.getTotalFramesStarted());
966                     assertTrue(String.format("The last repeating request surface timestamp " +
967                                     "%d must be less than or equal to the last " +
968                                     "onCaptureStarted " +
969                                     "timestamp %d", mSurfaceTexture.getTimestamp(),
970                             repeatingCaptureCallback.getLastTimestamp()),
971                             mSurfaceTexture.getTimestamp() <=
972                                     repeatingCaptureCallback.getLastTimestamp());
973 
974                 } finally {
975                     mTestRule.closeDevice(id);
976                     texturedSurface.release();
977                     extensionImageReader.close();
978                 }
979             }
980         }
981     }
982 
verifyJpegOrientation(Image img, Size jpegSize, int requestedOrientation)983     private void verifyJpegOrientation(Image img, Size jpegSize, int requestedOrientation)
984             throws IOException {
985         byte[] blobBuffer = getDataFromImage(img);
986         String blobFilename = mTestRule.getDebugFileNameBase() + "/verifyJpegKeys.jpeg";
987         dumpFile(blobFilename, blobBuffer);
988         ExifInterface exif = new ExifInterface(blobFilename);
989         int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, /*defaultValue*/0);
990         int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, /*defaultValue*/0);
991         Size exifSize = new Size(exifWidth, exifHeight);
992         int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
993                 /*defaultValue*/ ExifInterface.ORIENTATION_UNDEFINED);
994         final int ORIENTATION_MIN = ExifInterface.ORIENTATION_UNDEFINED;
995         final int ORIENTATION_MAX = ExifInterface.ORIENTATION_ROTATE_270;
996         assertTrue(String.format("Exif orientation must be in range of [%d, %d]",
997                 ORIENTATION_MIN, ORIENTATION_MAX),
998                 exifOrientation >= ORIENTATION_MIN && exifOrientation <= ORIENTATION_MAX);
999 
1000         /**
1001          * Device captured image doesn't respect the requested orientation,
1002          * which means it rotates the image buffer physically. Then we
1003          * should swap the exif width/height accordingly to compare.
1004          */
1005         boolean deviceRotatedImage = exifOrientation == ExifInterface.ORIENTATION_UNDEFINED;
1006 
1007         if (deviceRotatedImage) {
1008             // Case 1.
1009             boolean needSwap = (requestedOrientation % 180 == 90);
1010             if (needSwap) {
1011                 exifSize = new Size(exifHeight, exifWidth);
1012             }
1013         } else {
1014             // Case 2.
1015             assertEquals("Exif orientation should match requested orientation",
1016                     requestedOrientation, getExifOrientationInDegree(exifOrientation));
1017         }
1018 
1019         assertEquals("Exif size should match jpeg capture size", jpegSize, exifSize);
1020     }
1021 
getExifOrientationInDegree(int exifOrientation)1022     private static int getExifOrientationInDegree(int exifOrientation) {
1023         switch (exifOrientation) {
1024             case ExifInterface.ORIENTATION_NORMAL:
1025                 return 0;
1026             case ExifInterface.ORIENTATION_ROTATE_90:
1027                 return 90;
1028             case ExifInterface.ORIENTATION_ROTATE_180:
1029                 return 180;
1030             case ExifInterface.ORIENTATION_ROTATE_270:
1031                 return 270;
1032             default:
1033                 fail("It is impossible to get non 0, 90, 180, 270 degress exif" +
1034                         "info based on the request orientation range");
1035                 return -1;
1036         }
1037     }
1038 
1039     public static class SimpleCaptureCallback
1040             extends CameraExtensionSession.ExtensionCaptureCallback {
1041         private long mLastTimestamp = -1;
1042         private int mNumFramesArrived = 0;
1043         private int mNumFramesStarted = 0;
1044         private int mNumFramesFailed = 0;
1045         private boolean mNonIncreasingTimestamps = false;
1046         private HashSet<Long> mExpectedResultTimestamps = new HashSet<>();
1047         private final CameraExtensionSession.ExtensionCaptureCallback mProxy;
1048         private final AutoFocusStateMachine mAFStateMachine;
1049         private final FlashStateListener mFlashStateListener;
1050         private final AutoExposureStateListener mAEStateListener;
1051         private final HashSet<CaptureResult.Key> mSupportedResultKeys;
1052         private final CameraErrorCollector mCollector;
1053         private final boolean mPerFrameControl;
1054 
SimpleCaptureCallback(CameraExtensionSession.ExtensionCaptureCallback proxy, Set<CaptureResult.Key> supportedResultKeys, CameraErrorCollector errorCollector)1055         public SimpleCaptureCallback(CameraExtensionSession.ExtensionCaptureCallback proxy,
1056                 Set<CaptureResult.Key> supportedResultKeys, CameraErrorCollector errorCollector) {
1057             this(proxy, supportedResultKeys, errorCollector, null /*afListener*/,
1058                     null /*flashState*/, null /*aeState*/, false /*perFrameControl*/);
1059         }
1060 
SimpleCaptureCallback(CameraExtensionSession.ExtensionCaptureCallback proxy, Set<CaptureResult.Key> supportedResultKeys, CameraErrorCollector errorCollector, AutoFocusStateMachine afState, FlashStateListener flashState, AutoExposureStateListener aeState, boolean perFrameControl)1061         public SimpleCaptureCallback(CameraExtensionSession.ExtensionCaptureCallback proxy,
1062                 Set<CaptureResult.Key> supportedResultKeys, CameraErrorCollector errorCollector,
1063                 AutoFocusStateMachine afState, FlashStateListener flashState,
1064                 AutoExposureStateListener aeState, boolean perFrameControl) {
1065             mProxy = proxy;
1066             mSupportedResultKeys = new HashSet<>(supportedResultKeys);
1067             mCollector = errorCollector;
1068             mAFStateMachine = afState;
1069             mFlashStateListener = flashState;
1070             mAEStateListener = aeState;
1071             mPerFrameControl = perFrameControl;
1072         }
1073 
1074         @Override
onCaptureStarted(CameraExtensionSession session, CaptureRequest request, long timestamp)1075         public void onCaptureStarted(CameraExtensionSession session,
1076                                      CaptureRequest request, long timestamp) {
1077             mExpectedResultTimestamps.add(timestamp);
1078             if (timestamp < mLastTimestamp) {
1079                 mNonIncreasingTimestamps = true;
1080             }
1081             mLastTimestamp = timestamp;
1082             mNumFramesStarted++;
1083             if (mProxy != null) {
1084                 mProxy.onCaptureStarted(session, request, timestamp);
1085             }
1086         }
1087 
1088         @Override
onCaptureProcessStarted(CameraExtensionSession session, CaptureRequest request)1089         public void onCaptureProcessStarted(CameraExtensionSession session,
1090                                             CaptureRequest request) {
1091             mNumFramesArrived++;
1092             if (mProxy != null) {
1093                 mProxy.onCaptureProcessStarted(session, request);
1094             }
1095         }
1096 
1097         @Override
onCaptureFailed(CameraExtensionSession session, CaptureRequest request)1098         public void onCaptureFailed(CameraExtensionSession session,
1099                                     CaptureRequest request) {
1100             mNumFramesFailed++;
1101             if (mProxy != null) {
1102                 mProxy.onCaptureFailed(session, request);
1103             }
1104         }
1105 
1106         @Override
onCaptureSequenceAborted(CameraExtensionSession session, int sequenceId)1107         public void onCaptureSequenceAborted(CameraExtensionSession session,
1108                                              int sequenceId) {
1109             if (mProxy != null) {
1110                 mProxy.onCaptureSequenceAborted(session, sequenceId);
1111             }
1112         }
1113 
1114         @Override
onCaptureSequenceCompleted(CameraExtensionSession session, int sequenceId)1115         public void onCaptureSequenceCompleted(CameraExtensionSession session,
1116                                                int sequenceId) {
1117             if (mProxy != null) {
1118                 mProxy.onCaptureSequenceCompleted(session, sequenceId);
1119             }
1120         }
1121 
1122         @Override
onCaptureResultAvailable(CameraExtensionSession session, CaptureRequest request, TotalCaptureResult result)1123         public void  onCaptureResultAvailable(CameraExtensionSession session,
1124                 CaptureRequest request, TotalCaptureResult result) {
1125             final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f;
1126             if (mSupportedResultKeys.isEmpty()) {
1127                 mCollector.addMessage("Capture results not supported, but " +
1128                         "onCaptureResultAvailable still got triggered!");
1129                 return;
1130             }
1131 
1132             List<CaptureResult.Key<?>> resultKeys = result.getKeys();
1133             for (CaptureResult.Key<?> resultKey : resultKeys) {
1134                 mCollector.expectTrue("Capture result " + resultKey + " is not among the"
1135                         + " supported result keys!", mSupportedResultKeys.contains(resultKey));
1136             }
1137 
1138             Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
1139             assertNotNull(timeStamp);
1140             assertTrue("Capture result sensor timestamp: " + timeStamp + " must match "
1141                             + " with the timestamp passed to onCaptureStarted!",
1142                     mExpectedResultTimestamps.contains(timeStamp));
1143 
1144             Integer jpegOrientation = request.get(CaptureRequest.JPEG_ORIENTATION);
1145             if (jpegOrientation != null) {
1146                 Integer resultJpegOrientation = result.get(CaptureResult.JPEG_ORIENTATION);
1147                 mCollector.expectTrue("Request Jpeg orientation: " + jpegOrientation +
1148                         " doesn't match with result: " + resultJpegOrientation,
1149                         jpegOrientation.equals(resultJpegOrientation));
1150             }
1151 
1152             Byte jpegQuality = request.get(CaptureRequest.JPEG_QUALITY);
1153             if (jpegQuality != null) {
1154                 Byte resultJpegQuality = result.get(CaptureResult.JPEG_QUALITY);
1155                 mCollector.expectTrue("Request Jpeg quality: " + jpegQuality +
1156                         " doesn't match with result: " + resultJpegQuality,
1157                         jpegQuality.equals(resultJpegQuality));
1158             }
1159 
1160             if (resultKeys.contains(CaptureResult.CONTROL_ZOOM_RATIO) && mPerFrameControl) {
1161                 Float zoomRatio = request.get(CaptureRequest.CONTROL_ZOOM_RATIO);
1162                 if (zoomRatio != null) {
1163                     Float resultZoomRatio = result.get(CaptureResult.CONTROL_ZOOM_RATIO);
1164                     mCollector.expectTrue(
1165                             String.format("Request and result zoom ratio should be similar " +
1166                                     "(requested = %f, result = %f", zoomRatio, resultZoomRatio),
1167                             Math.abs(zoomRatio - resultZoomRatio) / zoomRatio <= ZOOM_ERROR_MARGIN);
1168                 }
1169             }
1170 
1171             if (mFlashStateListener != null) {
1172                 Integer flashMode = request.get(CaptureRequest.FLASH_MODE);
1173                 if ((flashMode != null) && mPerFrameControl) {
1174                     Integer resultFlashMode = result.get(CaptureResult.FLASH_MODE);
1175                     mCollector.expectTrue("Request flash mode: " + flashMode +
1176                                     " doesn't match with result: " + resultFlashMode,
1177                             flashMode.equals(resultFlashMode));
1178                 }
1179 
1180                 Integer flashState = result.get(CaptureResult.FLASH_STATE);
1181                 if (flashState != null) {
1182                     switch (flashState) {
1183                         case CaptureResult.FLASH_STATE_UNAVAILABLE:
1184                             mFlashStateListener.onUnavailable();
1185                             break;
1186                         case CaptureResult.FLASH_STATE_FIRED:
1187                             mFlashStateListener.onFired();
1188                             break;
1189                         case CaptureResult.FLASH_STATE_CHARGING:
1190                             mFlashStateListener.onCharging();
1191                             break;
1192                         case CaptureResult.FLASH_STATE_PARTIAL:
1193                             mFlashStateListener.onPartial();
1194                             break;
1195                         case CaptureResult.FLASH_STATE_READY:
1196                             mFlashStateListener.onReady();
1197                             break;
1198                         default:
1199                             mCollector.addMessage("Unexpected flash state: " + flashState);
1200                     }
1201                 }
1202             }
1203 
1204             if (mAEStateListener != null) {
1205                 Integer aeMode = request.get(CaptureRequest.CONTROL_AE_MODE);
1206                 if ((aeMode != null) && mPerFrameControl) {
1207                     Integer resultAeMode = result.get(CaptureResult.CONTROL_AE_MODE);
1208                     mCollector.expectTrue("Request AE mode: " + aeMode +
1209                                     " doesn't match with result: " + resultAeMode,
1210                             aeMode.equals(resultAeMode));
1211                 }
1212 
1213                 Boolean aeLock = request.get(CaptureRequest.CONTROL_AE_LOCK);
1214                 if ((aeLock != null) && mPerFrameControl) {
1215                     Boolean resultAeLock = result.get(CaptureResult.CONTROL_AE_LOCK);
1216                     mCollector.expectTrue("Request AE lock: " + aeLock +
1217                                     " doesn't match with result: " + resultAeLock,
1218                             aeLock.equals(resultAeLock));
1219                 }
1220 
1221                 Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
1222                 if (aeState != null) {
1223                     switch (aeState) {
1224                         case CaptureResult.CONTROL_AE_STATE_CONVERGED:
1225                             mAEStateListener.onConverged();
1226                             break;
1227                         case CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED:
1228                             mAEStateListener.onFlashRequired();
1229                             break;
1230                         case CaptureResult.CONTROL_AE_STATE_INACTIVE:
1231                             mAEStateListener.onInactive();
1232                             break;
1233                         case CaptureResult.CONTROL_AE_STATE_LOCKED:
1234                             mAEStateListener.onLocked();
1235                             break;
1236                         case CaptureResult.CONTROL_AE_STATE_PRECAPTURE:
1237                             mAEStateListener.onPrecapture();
1238                             break;
1239                         case CaptureResult.CONTROL_AE_STATE_SEARCHING:
1240                             mAEStateListener.onSearching();
1241                             break;
1242                         default:
1243                             mCollector.addMessage("Unexpected AE state: " + aeState);
1244                     }
1245                 }
1246             }
1247 
1248             if (mAFStateMachine != null) {
1249                 Integer afMode = request.get(CaptureRequest.CONTROL_AF_MODE);
1250                 if ((afMode != null) && mPerFrameControl) {
1251                     Integer resultAfMode = result.get(CaptureResult.CONTROL_AF_MODE);
1252                     mCollector.expectTrue("Request AF mode: " + afMode +
1253                                     " doesn't match with result: " + resultAfMode,
1254                             afMode.equals(resultAfMode));
1255                 }
1256 
1257                 MeteringRectangle[] afRegions = request.get(CaptureRequest.CONTROL_AF_REGIONS);
1258                 if ((afRegions != null) && mPerFrameControl) {
1259                     MeteringRectangle[] resultAfRegions = result.get(
1260                             CaptureResult.CONTROL_AF_REGIONS);
1261                     mCollector.expectMeteringRegionsAreSimilar(
1262                             "AF regions in result and request should be similar",
1263                             afRegions, resultAfRegions, METERING_REGION_ERROR_PERCENT_DELTA);
1264                 }
1265 
1266                 Integer afTrigger = request.get(CaptureRequest.CONTROL_AF_TRIGGER);
1267                 if ((afTrigger != null) && mPerFrameControl) {
1268                     Integer resultAfTrigger = result.get(CaptureResult.CONTROL_AF_TRIGGER);
1269                     mCollector.expectTrue("Request AF trigger: " + afTrigger +
1270                                     " doesn't match with result: " + resultAfTrigger,
1271                             afTrigger.equals(resultAfTrigger));
1272                 }
1273 
1274                 mAFStateMachine.onCaptureCompleted(result);
1275             }
1276 
1277             if (mProxy != null) {
1278                 mProxy.onCaptureResultAvailable(session, request, result);
1279             }
1280         }
1281 
getTotalFramesArrived()1282         public int getTotalFramesArrived() {
1283             return mNumFramesArrived;
1284         }
1285 
getTotalFramesStarted()1286         public int getTotalFramesStarted() {
1287             return mNumFramesStarted;
1288         }
1289 
getTotalFramesFailed()1290         public int getTotalFramesFailed() {
1291             return mNumFramesFailed;
1292         }
1293 
getLastTimestamp()1294         public long getLastTimestamp() throws IllegalStateException {
1295             if (mNonIncreasingTimestamps) {
1296                 throw new IllegalStateException("Non-monotonically increasing timestamps!");
1297             }
1298             return mLastTimestamp;
1299         }
1300     }
1301 
1302     public interface AutoFocusStateListener {
onDone(boolean success)1303         void onDone(boolean success);
onScan()1304         void onScan();
onInactive()1305         void onInactive();
1306     }
1307 
1308     private class TestAutoFocusProxy implements AutoFocusStateMachine.AutoFocusStateListener {
1309         private final AutoFocusStateListener mListener;
1310 
TestAutoFocusProxy(AutoFocusStateListener listener)1311         TestAutoFocusProxy(AutoFocusStateListener listener) {
1312             mListener = listener;
1313         }
1314 
1315         @Override
onAutoFocusSuccess(CaptureResult result, boolean locked)1316         public void onAutoFocusSuccess(CaptureResult result, boolean locked) {
1317             mListener.onDone(true);
1318         }
1319 
1320         @Override
onAutoFocusFail(CaptureResult result, boolean locked)1321         public void onAutoFocusFail(CaptureResult result, boolean locked) {
1322             mListener.onDone(false);
1323         }
1324 
1325         @Override
onAutoFocusScan(CaptureResult result)1326         public void onAutoFocusScan(CaptureResult result) {
1327             mListener.onScan();
1328         }
1329 
1330         @Override
onAutoFocusInactive(CaptureResult result)1331         public void onAutoFocusInactive(CaptureResult result) {
1332             mListener.onInactive();
1333         }
1334     }
1335 
1336     // Verify that camera extension sessions can support AF and AF metering controls. The test
1337     // goal is to check that AF related controls and results are supported and can be used to
1338     // lock the AF state and not to do an exhaustive check of the AF state transitions or manual AF.
1339     @Test
testAFMetering()1340     public void testAFMetering() throws Exception {
1341         final CaptureRequest.Key[] FOCUS_CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_AF_MODE,
1342                 CaptureRequest.CONTROL_AF_REGIONS, CaptureRequest.CONTROL_AF_TRIGGER};
1343         final CaptureResult.Key[] FOCUS_CAPTURE_RESULT_SET = {CaptureResult.CONTROL_AF_MODE,
1344                 CaptureResult.CONTROL_AF_REGIONS, CaptureResult.CONTROL_AF_TRIGGER,
1345                 CaptureResult.CONTROL_AF_STATE};
1346         final int METERING_REGION_SCALE_RATIO = 8;
1347         final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 6000;
1348         for (String id : mCameraIdsUnderTest) {
1349             StaticMetadata staticMeta =
1350                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
1351             if (!staticMeta.isColorOutputSupported()) {
1352                 continue;
1353             }
1354             if (!staticMeta.hasFocuser()) {
1355                 continue;
1356             }
1357 
1358             boolean perFrameControl = staticMeta.isPerFrameControlSupported();
1359             final Rect activeArraySize = staticMeta.getActiveArraySizeChecked();
1360             int regionWidth = activeArraySize.width() / METERING_REGION_SCALE_RATIO - 1;
1361             int regionHeight = activeArraySize.height() / METERING_REGION_SCALE_RATIO - 1;
1362             int centerX = activeArraySize.width() / 2;
1363             int centerY = activeArraySize.height() / 2;
1364 
1365             // Center region
1366             MeteringRectangle[] afMeteringRects = {new MeteringRectangle(
1367                     centerX - regionWidth / 2, centerY - regionHeight / 2,
1368                     regionWidth, regionHeight,
1369                     MeteringRectangle.METERING_WEIGHT_MAX)};
1370 
1371             updatePreviewSurfaceTexture();
1372             CameraExtensionCharacteristics extensionChars =
1373                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
1374             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
1375             for (Integer extension : supportedExtensions) {
1376                 Set<CaptureRequest.Key> supportedRequestKeys =
1377                         extensionChars.getAvailableCaptureRequestKeys(extension);
1378                 if (!supportedRequestKeys.containsAll(Arrays.asList(FOCUS_CAPTURE_REQUEST_SET))) {
1379                     continue;
1380                 }
1381                 Set<CaptureResult.Key> supportedResultKeys =
1382                         extensionChars.getAvailableCaptureResultKeys(extension);
1383                 assertTrue(supportedResultKeys.containsAll(
1384                         Arrays.asList(FOCUS_CAPTURE_RESULT_SET)));
1385 
1386                 List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
1387                         mSurfaceTexture.getClass());
1388                 Size maxSize =
1389                         CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
1390                 mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(),
1391                         maxSize.getHeight());
1392                 Surface texturedSurface = new Surface(mSurfaceTexture);
1393 
1394                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
1395                 outputConfigs.add(new OutputConfiguration(texturedSurface));
1396 
1397                 BlockingExtensionSessionCallback sessionListener =
1398                         new BlockingExtensionSessionCallback(mock(
1399                                 CameraExtensionSession.StateCallback.class));
1400                 ExtensionSessionConfiguration configuration =
1401                         new ExtensionSessionConfiguration(extension, outputConfigs,
1402                                 new HandlerExecutor(mTestRule.getHandler()),
1403                                 sessionListener);
1404 
1405                 try {
1406                     mTestRule.openDevice(id);
1407                     CameraDevice camera = mTestRule.getCamera();
1408                     camera.createExtensionSession(configuration);
1409                     CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
1410                             SESSION_CONFIGURE_TIMEOUT_MS);
1411                     assertNotNull(extensionSession);
1412 
1413                     // Check passive AF
1414                     AutoFocusStateListener mockAFListener =
1415                             mock(AutoFocusStateListener.class);
1416                     AutoFocusStateMachine afState = new AutoFocusStateMachine(
1417                             new TestAutoFocusProxy(mockAFListener));
1418                     CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
1419                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
1420                     SimpleCaptureCallback repeatingCaptureCallback =
1421                             new SimpleCaptureCallback(repeatingCallbackMock,
1422                                     extensionChars.getAvailableCaptureResultKeys(extension),
1423                                     mCollector, afState, null /*flashState*/,
1424                                     null /*aeState*/, perFrameControl);
1425 
1426                     CaptureRequest.Builder captureBuilder =
1427                             mTestRule.getCamera().createCaptureRequest(
1428                                     android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
1429                     captureBuilder.addTarget(texturedSurface);
1430                     afState.setPassiveAutoFocus(true /*picture*/, captureBuilder);
1431                     captureBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, afMeteringRects);
1432                     CaptureRequest request = captureBuilder.build();
1433                     int passiveSequenceId = extensionSession.setRepeatingRequest(request,
1434                             new HandlerExecutor(mTestRule.getHandler()), repeatingCaptureCallback);
1435                     assertTrue(passiveSequenceId > 0);
1436 
1437                     verify(repeatingCallbackMock,
1438                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
1439                             .onCaptureResultAvailable(eq(extensionSession), eq(request),
1440                                     any(TotalCaptureResult.class));
1441 
1442                     verify(mockAFListener,
1443                             timeout(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS).atLeastOnce())
1444                             .onDone(anyBoolean());
1445 
1446                     // Check active AF
1447                     mockAFListener = mock(AutoFocusStateListener.class);
1448                     CameraExtensionSession.ExtensionCaptureCallback callbackMock = mock(
1449                             CameraExtensionSession.ExtensionCaptureCallback.class);
1450                     AutoFocusStateMachine activeAFState = new AutoFocusStateMachine(
1451                             new TestAutoFocusProxy(mockAFListener));
1452                     CameraExtensionSession.ExtensionCaptureCallback captureCallback =
1453                             new SimpleCaptureCallback(callbackMock,
1454                                     extensionChars.getAvailableCaptureResultKeys(extension),
1455                                     mCollector, activeAFState, null /*flashState*/,
1456                                     null /*aeState*/, perFrameControl);
1457 
1458                     CaptureRequest.Builder triggerBuilder =
1459                             mTestRule.getCamera().createCaptureRequest(
1460                                     android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
1461                     triggerBuilder.addTarget(texturedSurface);
1462                     afState.setActiveAutoFocus(captureBuilder, triggerBuilder);
1463                     afState.unlockAutoFocus(captureBuilder, triggerBuilder);
1464                     triggerBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, afMeteringRects);
1465                     request = captureBuilder.build();
1466                     int activeSequenceId = extensionSession.setRepeatingRequest(request,
1467                             new HandlerExecutor(mTestRule.getHandler()), captureCallback);
1468                     assertTrue((activeSequenceId > 0) &&
1469                             (activeSequenceId != passiveSequenceId));
1470 
1471                     verify(callbackMock,
1472                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
1473                             .onCaptureResultAvailable(eq(extensionSession), eq(request),
1474                                     any(TotalCaptureResult.class));
1475 
1476                     CaptureRequest triggerRequest = triggerBuilder.build();
1477                     reset(mockAFListener);
1478                     int triggerSequenceId = extensionSession.capture(triggerRequest,
1479                             new HandlerExecutor(mTestRule.getHandler()), captureCallback);
1480                     assertTrue((triggerSequenceId > 0) &&
1481                             (activeSequenceId != triggerSequenceId) &&
1482                             (triggerSequenceId != passiveSequenceId));
1483 
1484                     verify(callbackMock,
1485                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
1486                             .onCaptureResultAvailable(eq(extensionSession), eq(triggerRequest),
1487                                     any(TotalCaptureResult.class));
1488 
1489                     afState.lockAutoFocus(captureBuilder, triggerBuilder);
1490                     triggerRequest = triggerBuilder.build();
1491                     reset(mockAFListener);
1492                     extensionSession.capture(triggerRequest,
1493                             new HandlerExecutor(mTestRule.getHandler()), captureCallback);
1494 
1495                     verify(callbackMock,
1496                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
1497                             .onCaptureResultAvailable(eq(extensionSession), eq(triggerRequest),
1498                                     any(TotalCaptureResult.class));
1499 
1500                     verify(mockAFListener, timeout(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS)
1501                             .atLeast(1)).onDone(anyBoolean());
1502 
1503                     extensionSession.stopRepeating();
1504 
1505                     extensionSession.close();
1506 
1507                     sessionListener.getStateWaiter().waitForState(
1508                             BlockingExtensionSessionCallback.SESSION_CLOSED,
1509                             SESSION_CLOSE_TIMEOUT_MS);
1510                 } finally {
1511                     mTestRule.closeDevice(id);
1512                     texturedSurface.release();
1513                 }
1514             }
1515         }
1516     }
1517 
1518     // Verify that camera extension sessions can support the zoom ratio control.
1519     @Test
testZoomRatio()1520     public void testZoomRatio() throws Exception {
1521         final CaptureRequest.Key[] ZOOM_CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_ZOOM_RATIO};
1522         final CaptureResult.Key[] ZOOM_CAPTURE_RESULT_SET = {CaptureResult.CONTROL_ZOOM_RATIO};
1523         final int ZOOM_RATIO_STEPS = 10;
1524         for (String id : mCameraIdsUnderTest) {
1525             StaticMetadata staticMeta =
1526                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
1527             if (!staticMeta.isColorOutputSupported()) {
1528                 continue;
1529             }
1530             Range<Float> zoomRatioRange = staticMeta.getZoomRatioRangeChecked();
1531             if (zoomRatioRange.getUpper().equals(zoomRatioRange.getLower())) {
1532                 continue;
1533             }
1534 
1535             final float maxZoom = staticMeta.getAvailableMaxDigitalZoomChecked();
1536             if (Math.abs(maxZoom - 1.0f) < ZOOM_ERROR_MARGIN) {
1537                 return;
1538             }
1539 
1540             Float zoomStep  =
1541                     (zoomRatioRange.getUpper() - zoomRatioRange.getLower()) / ZOOM_RATIO_STEPS;
1542             if (zoomStep < ZOOM_ERROR_MARGIN) {
1543                 continue;
1544             }
1545 
1546             ArrayList<Float> candidateZoomRatios = new ArrayList<>(ZOOM_RATIO_STEPS);
1547             for (int step = 0; step < (ZOOM_RATIO_STEPS - 1); step++) {
1548                 candidateZoomRatios.add(step, zoomRatioRange.getLower() + step * zoomStep);
1549             }
1550             candidateZoomRatios.add(ZOOM_RATIO_STEPS - 1, zoomRatioRange.getUpper());
1551 
1552             updatePreviewSurfaceTexture();
1553             CameraExtensionCharacteristics extensionChars =
1554                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
1555             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
1556             for (Integer extension : supportedExtensions) {
1557                 Set<CaptureRequest.Key> supportedRequestKeys =
1558                         extensionChars.getAvailableCaptureRequestKeys(extension);
1559                 if (!supportedRequestKeys.containsAll(Arrays.asList(ZOOM_CAPTURE_REQUEST_SET))) {
1560                     continue;
1561                 }
1562                 Set<CaptureResult.Key> supportedResultKeys =
1563                         extensionChars.getAvailableCaptureResultKeys(extension);
1564                 assertTrue(supportedResultKeys.containsAll(
1565                         Arrays.asList(ZOOM_CAPTURE_RESULT_SET)));
1566 
1567                 List<Size> extensionSizes = extensionChars.getExtensionSupportedSizes(extension,
1568                         mSurfaceTexture.getClass());
1569                 Size maxSize =
1570                         CameraTestUtils.getMaxSize(extensionSizes.toArray(new Size[0]));
1571                 mSurfaceTexture.setDefaultBufferSize(maxSize.getWidth(),
1572                         maxSize.getHeight());
1573                 Surface texturedSurface = new Surface(mSurfaceTexture);
1574 
1575                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
1576                 outputConfigs.add(new OutputConfiguration(texturedSurface));
1577 
1578                 BlockingExtensionSessionCallback sessionListener =
1579                         new BlockingExtensionSessionCallback(mock(
1580                                 CameraExtensionSession.StateCallback.class));
1581                 ExtensionSessionConfiguration configuration =
1582                         new ExtensionSessionConfiguration(extension, outputConfigs,
1583                                 new HandlerExecutor(mTestRule.getHandler()),
1584                                 sessionListener);
1585 
1586                 try {
1587                     mTestRule.openDevice(id);
1588                     CameraDevice camera = mTestRule.getCamera();
1589                     camera.createExtensionSession(configuration);
1590                     CameraExtensionSession extensionSession = sessionListener.waitAndGetSession(
1591                             SESSION_CONFIGURE_TIMEOUT_MS);
1592                     assertNotNull(extensionSession);
1593 
1594                     CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
1595                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
1596                     SimpleCaptureCallback repeatingCaptureCallback =
1597                             new SimpleCaptureCallback(repeatingCallbackMock,
1598                                     extensionChars.getAvailableCaptureResultKeys(extension),
1599                                     mCollector);
1600 
1601                     CaptureRequest.Builder captureBuilder =
1602                             mTestRule.getCamera().createCaptureRequest(
1603                                     android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
1604                     captureBuilder.addTarget(texturedSurface);
1605                     for (Float currentZoomRatio : candidateZoomRatios) {
1606                         captureBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, currentZoomRatio);
1607                         CaptureRequest request = captureBuilder.build();
1608 
1609                         int seqId = extensionSession.setRepeatingRequest(request,
1610                                 new HandlerExecutor(mTestRule.getHandler()),
1611                                 repeatingCaptureCallback);
1612                         assertTrue(seqId > 0);
1613 
1614                         verify(repeatingCallbackMock,
1615                                 timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
1616                                 .onCaptureResultAvailable(eq(extensionSession), eq(request),
1617                                         any(TotalCaptureResult.class));
1618                     }
1619 
1620                     extensionSession.stopRepeating();
1621 
1622                     extensionSession.close();
1623 
1624                     sessionListener.getStateWaiter().waitForState(
1625                             BlockingExtensionSessionCallback.SESSION_CLOSED,
1626                             SESSION_CLOSE_TIMEOUT_MS);
1627                 } finally {
1628                     mTestRule.closeDevice(id);
1629                     texturedSurface.release();
1630                 }
1631             }
1632         }
1633     }
1634 
1635     public interface FlashStateListener {
onFired()1636         public void onFired();
onReady()1637         public void onReady();
onCharging()1638         public void onCharging();
onPartial()1639         public void onPartial();
onUnavailable()1640         public void onUnavailable();
1641     }
1642 
1643     public interface AutoExposureStateListener {
onInactive()1644       public void onInactive();
onSearching()1645       public void onSearching();
onConverged()1646       public void onConverged();
onLocked()1647       public void onLocked();
onFlashRequired()1648       public void onFlashRequired();
onPrecapture()1649       public void onPrecapture();
1650     }
1651 
1652     // Verify that camera extension sessions can support Flash related controls. The test
1653     // goal is to check that Flash controls and results are supported and can be used to
1654     // turn on torch, run the pre-capture sequence and active the main flash.
1655     @Test
testFlash()1656     public void testFlash() throws Exception {
1657         final CaptureRequest.Key[] FLASH_CAPTURE_REQUEST_SET = {CaptureRequest.CONTROL_AE_MODE,
1658                 CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_LOCK,
1659                 CaptureRequest.FLASH_MODE};
1660         final CaptureResult.Key[] FLASH_CAPTURE_RESULT_SET = {CaptureResult.CONTROL_AE_MODE,
1661                 CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureResult.CONTROL_AE_LOCK,
1662                 CaptureResult.CONTROL_AE_STATE, CaptureResult.FLASH_MODE,
1663                 CaptureResult.FLASH_STATE};
1664         for (String id : mCameraIdsUnderTest) {
1665             StaticMetadata staticMeta =
1666                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
1667             if (!staticMeta.isColorOutputSupported()) {
1668                 continue;
1669             }
1670             if (!staticMeta.hasFlash()) {
1671                 continue;
1672             }
1673 
1674             boolean perFrameControl = staticMeta.isPerFrameControlSupported();
1675             updatePreviewSurfaceTexture();
1676             CameraExtensionCharacteristics extensionChars =
1677                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
1678             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
1679             for (Integer extension : supportedExtensions) {
1680                 Set<CaptureRequest.Key> supportedRequestKeys =
1681                         extensionChars.getAvailableCaptureRequestKeys(extension);
1682                 if (!supportedRequestKeys.containsAll(Arrays.asList(FLASH_CAPTURE_REQUEST_SET))) {
1683                     continue;
1684                 }
1685                 Set<CaptureResult.Key> supportedResultKeys =
1686                         extensionChars.getAvailableCaptureResultKeys(extension);
1687                 assertTrue(supportedResultKeys.containsAll(
1688                         Arrays.asList(FLASH_CAPTURE_RESULT_SET)));
1689 
1690                 int captureFormat = ImageFormat.JPEG;
1691                 List<Size> captureSizes = extensionChars.getExtensionSupportedSizes(extension,
1692                         captureFormat);
1693                 assertFalse("No Jpeg output supported", captureSizes.isEmpty());
1694                 Size captureMaxSize = captureSizes.get(0);
1695 
1696                 SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false, 1);
1697                 ImageReader extensionImageReader = CameraTestUtils.makeImageReader(
1698                         captureMaxSize, captureFormat, /*maxImages*/ 1, imageListener,
1699                         mTestRule.getHandler());
1700                 Surface imageReaderSurface = extensionImageReader.getSurface();
1701                 OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
1702                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
1703                 outputConfigs.add(readerOutput);
1704 
1705                 List<Size> repeatingSizes = extensionChars.getExtensionSupportedSizes(extension,
1706                         mSurfaceTexture.getClass());
1707                 Size previewSize = repeatingSizes.get(0);
1708 
1709                 mSurfaceTexture.setDefaultBufferSize(previewSize.getWidth(),
1710                         previewSize.getHeight());
1711                 Surface texturedSurface = new Surface(mSurfaceTexture);
1712                 outputConfigs.add(new OutputConfiguration(texturedSurface));
1713 
1714                 BlockingExtensionSessionCallback sessionListener =
1715                         new BlockingExtensionSessionCallback(mock(
1716                                 CameraExtensionSession.StateCallback.class));
1717                 ExtensionSessionConfiguration configuration =
1718                         new ExtensionSessionConfiguration(extension, outputConfigs,
1719                                 new HandlerExecutor(mTestRule.getHandler()),
1720                                 sessionListener);
1721                 try {
1722                     mTestRule.openDevice(id);
1723                     CameraDevice camera = mTestRule.getCamera();
1724                     camera.createExtensionSession(configuration);
1725                     CameraExtensionSession extensionSession =
1726                             sessionListener.waitAndGetSession(
1727                                     SESSION_CONFIGURE_TIMEOUT_MS);
1728                     assertNotNull(extensionSession);
1729 
1730                     // Test torch on and off
1731                     CaptureRequest.Builder captureBuilder =
1732                             mTestRule.getCamera().createCaptureRequest(
1733                                     android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
1734                     captureBuilder.addTarget(texturedSurface);
1735                     captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
1736                             CameraMetadata.CONTROL_AE_MODE_ON);
1737                     captureBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
1738                     captureBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
1739                     FlashStateListener mockFlashListener = mock(FlashStateListener.class);
1740                     AutoExposureStateListener mockAEStateListener =
1741                             mock(AutoExposureStateListener.class);
1742                     CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
1743                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
1744                     SimpleCaptureCallback repeatingCaptureCallback =
1745                             new SimpleCaptureCallback(repeatingCallbackMock,
1746                                     extensionChars.getAvailableCaptureResultKeys(extension),
1747                                     mCollector, null /*afState*/, mockFlashListener,
1748                                     mockAEStateListener, perFrameControl);
1749                     CaptureRequest repeatingRequest = captureBuilder.build();
1750                     int repeatingSequenceId =
1751                             extensionSession.setRepeatingRequest(repeatingRequest,
1752                                     new HandlerExecutor(mTestRule.getHandler()),
1753                                     repeatingCaptureCallback);
1754                     assertTrue(repeatingSequenceId > 0);
1755 
1756                     verify(repeatingCallbackMock,
1757                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
1758                             .onCaptureResultAvailable(eq(extensionSession),
1759                                     eq(repeatingRequest), any(TotalCaptureResult.class));
1760                     verify(mockFlashListener,
1761                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onFired();
1762 
1763                     captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
1764                             CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
1765                     captureBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
1766                     repeatingRequest = captureBuilder.build();
1767                     reset(mockFlashListener);
1768                     repeatingSequenceId = extensionSession.setRepeatingRequest(repeatingRequest,
1769                                     new HandlerExecutor(mTestRule.getHandler()),
1770                                     repeatingCaptureCallback);
1771                     assertTrue(repeatingSequenceId > 0);
1772 
1773                     verify(repeatingCallbackMock,
1774                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
1775                             .onCaptureResultAvailable(eq(extensionSession),
1776                                     eq(repeatingRequest), any(TotalCaptureResult.class));
1777                     verify(mockFlashListener,
1778                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onReady();
1779 
1780                     // Test AE pre-capture sequence
1781                     captureBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
1782                     captureBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
1783                             CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START);
1784                     CaptureRequest triggerRequest = captureBuilder.build();
1785                     int triggerSeqId = extensionSession.capture(triggerRequest,
1786                             new HandlerExecutor(mTestRule.getHandler()), repeatingCaptureCallback);
1787                     assertTrue(triggerSeqId > 0);
1788 
1789                     verify(repeatingCallbackMock,
1790                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce())
1791                             .onCaptureResultAvailable(eq(extensionSession),
1792                                     eq(triggerRequest), any(TotalCaptureResult.class));
1793                     verify(mockAEStateListener,
1794                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onPrecapture();
1795                     verify(mockAEStateListener,
1796                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onConverged();
1797 
1798                     // Test main flash
1799                     captureBuilder = mTestRule.getCamera().createCaptureRequest(
1800                             android.hardware.camera2.CameraDevice.TEMPLATE_STILL_CAPTURE);
1801                     captureBuilder.addTarget(imageReaderSurface);
1802                     captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
1803                             CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
1804                     captureBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
1805                     captureBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE);
1806                     CameraExtensionSession.ExtensionCaptureCallback mockCaptureCallback =
1807                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
1808                     reset(mockFlashListener);
1809                     SimpleCaptureCallback captureCallback =
1810                             new SimpleCaptureCallback(mockCaptureCallback,
1811                                     extensionChars.getAvailableCaptureResultKeys(extension),
1812                                     mCollector, null /*afState*/, mockFlashListener,
1813                                     mockAEStateListener, perFrameControl);
1814 
1815                     CaptureRequest captureRequest = captureBuilder.build();
1816                     int captureSequenceId = extensionSession.capture(captureRequest,
1817                             new HandlerExecutor(mTestRule.getHandler()), captureCallback);
1818                     assertTrue(captureSequenceId > 0);
1819 
1820                     Image img =
1821                             imageListener.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
1822                     validateImage(img, captureMaxSize.getWidth(),
1823                             captureMaxSize.getHeight(), captureFormat, null);
1824                     long imgTs = img.getTimestamp();
1825                     img.close();
1826 
1827                     verify(mockCaptureCallback,
1828                             timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
1829                             .onCaptureStarted(eq(extensionSession), eq(captureRequest), eq(imgTs));
1830                     verify(mockCaptureCallback,
1831                             timeout(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS).times(1))
1832                             .onCaptureResultAvailable(eq(extensionSession),
1833                                     eq(captureRequest), any(TotalCaptureResult.class));
1834                     verify(mockFlashListener,
1835                             timeout(REPEATING_REQUEST_TIMEOUT_MS).atLeastOnce()).onFired();
1836 
1837                     extensionSession.stopRepeating();
1838 
1839                     extensionSession.close();
1840 
1841                     sessionListener.getStateWaiter().waitForState(
1842                             BlockingExtensionSessionCallback.SESSION_CLOSED,
1843                             SESSION_CLOSE_TIMEOUT_MS);
1844                 } finally {
1845                     mTestRule.closeDevice(id);
1846                     texturedSurface.release();
1847                     extensionImageReader.close();
1848                 }
1849             }
1850         }
1851     }
1852 
1853     @Test
testIllegalArguments()1854     public void testIllegalArguments() throws Exception {
1855         for (String id : mCameraIdsUnderTest) {
1856             StaticMetadata staticMeta =
1857                     new StaticMetadata(mTestRule.getCameraManager().getCameraCharacteristics(id));
1858             if (!staticMeta.isColorOutputSupported()) {
1859                 continue;
1860             }
1861             updatePreviewSurfaceTexture();
1862             CameraExtensionCharacteristics extensionChars =
1863                     mTestRule.getCameraManager().getCameraExtensionCharacteristics(id);
1864             List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
1865             for (Integer extension : supportedExtensions) {
1866                 List<OutputConfiguration> outputConfigs = new ArrayList<>();
1867                 BlockingExtensionSessionCallback sessionListener =
1868                         new BlockingExtensionSessionCallback(mock(
1869                                 CameraExtensionSession.StateCallback.class));
1870                 ExtensionSessionConfiguration configuration =
1871                         new ExtensionSessionConfiguration(extension, outputConfigs,
1872                                 new HandlerExecutor(mTestRule.getHandler()),
1873                                 sessionListener);
1874 
1875                 try {
1876                     mTestRule.openDevice(id);
1877                     CameraDevice camera = mTestRule.getCamera();
1878                     try {
1879                         camera.createExtensionSession(configuration);
1880                         fail("should get IllegalArgumentException due to absent output surfaces");
1881                     } catch (IllegalArgumentException e) {
1882                         // Expected, we can proceed further
1883                     }
1884 
1885                     int captureFormat = ImageFormat.YUV_420_888;
1886                     List<Size> captureSizes = extensionChars.getExtensionSupportedSizes(extension,
1887                             captureFormat);
1888                     if (captureSizes.isEmpty()) {
1889                         captureFormat = ImageFormat.JPEG;
1890                         captureSizes = extensionChars.getExtensionSupportedSizes(extension,
1891                                 captureFormat);
1892                     }
1893                     Size captureMaxSize =
1894                             CameraTestUtils.getMaxSize(captureSizes.toArray(new Size[0]));
1895 
1896                     mSurfaceTexture.setDefaultBufferSize(1, 1);
1897                     Surface texturedSurface = new Surface(mSurfaceTexture);
1898                     outputConfigs.add(new OutputConfiguration(texturedSurface));
1899                     configuration = new ExtensionSessionConfiguration(extension, outputConfigs,
1900                             new HandlerExecutor(mTestRule.getHandler()), sessionListener);
1901 
1902                     try {
1903                         camera.createExtensionSession(configuration);
1904                         fail("should get IllegalArgumentException due to illegal repeating request"
1905                                 + " output surface");
1906                     } catch (IllegalArgumentException e) {
1907                         // Expected, we can proceed further
1908                     } finally {
1909                         outputConfigs.clear();
1910                     }
1911 
1912                     SimpleImageReaderListener imageListener = new SimpleImageReaderListener(false,
1913                             1);
1914                     Size invalidCaptureSize = new Size(1, 1);
1915                     ImageReader extensionImageReader = CameraTestUtils.makeImageReader(
1916                             invalidCaptureSize, captureFormat, /*maxImages*/ 1,
1917                             imageListener, mTestRule.getHandler());
1918                     Surface imageReaderSurface = extensionImageReader.getSurface();
1919                     OutputConfiguration readerOutput = new OutputConfiguration(imageReaderSurface);
1920                     outputConfigs.add(readerOutput);
1921                     configuration = new ExtensionSessionConfiguration(extension, outputConfigs,
1922                             new HandlerExecutor(mTestRule.getHandler()), sessionListener);
1923 
1924                     try{
1925                         camera.createExtensionSession(configuration);
1926                         fail("should get IllegalArgumentException due to illegal multi-frame"
1927                                 + " request output surface");
1928                     } catch (IllegalArgumentException e) {
1929                         // Expected, we can proceed further
1930                     } finally {
1931                         outputConfigs.clear();
1932                         extensionImageReader.close();
1933                     }
1934 
1935                     // Pick a supported preview/repeating size with aspect ratio close to the
1936                     // multi-frame capture size
1937                     List<Size> repeatingSizes = extensionChars.getExtensionSupportedSizes(extension,
1938                             mSurfaceTexture.getClass());
1939                     Size maxRepeatingSize =
1940                             CameraTestUtils.getMaxSize(repeatingSizes.toArray(new Size[0]));
1941                     List<Size> previewSizes = getSupportedPreviewSizes(id,
1942                             mTestRule.getCameraManager(),
1943                             getPreviewSizeBound(mTestRule.getWindowManager(), PREVIEW_SIZE_BOUND));
1944                     List<Size> supportedPreviewSizes =
1945                             previewSizes.stream().filter(repeatingSizes::contains).collect(
1946                                     Collectors.toList());
1947                     if (!supportedPreviewSizes.isEmpty()) {
1948                         float targetAr =
1949                                 ((float) captureMaxSize.getWidth()) / captureMaxSize.getHeight();
1950                         for (Size s : supportedPreviewSizes) {
1951                             float currentAr = ((float) s.getWidth()) / s.getHeight();
1952                             if (Math.abs(targetAr - currentAr) < 0.01) {
1953                                 maxRepeatingSize = s;
1954                                 break;
1955                             }
1956                         }
1957                     }
1958 
1959                     imageListener = new SimpleImageReaderListener(false, 1);
1960                     extensionImageReader = CameraTestUtils.makeImageReader(captureMaxSize,
1961                             captureFormat, /*maxImages*/ 1, imageListener, mTestRule.getHandler());
1962                     imageReaderSurface = extensionImageReader.getSurface();
1963                     readerOutput = new OutputConfiguration(imageReaderSurface);
1964                     outputConfigs.add(readerOutput);
1965 
1966                     mSurfaceTexture.setDefaultBufferSize(maxRepeatingSize.getWidth(),
1967                             maxRepeatingSize.getHeight());
1968                     texturedSurface = new Surface(mSurfaceTexture);
1969                     outputConfigs.add(new OutputConfiguration(texturedSurface));
1970 
1971                     configuration = new ExtensionSessionConfiguration(extension, outputConfigs,
1972                             new HandlerExecutor(mTestRule.getHandler()), sessionListener);
1973                     camera.createExtensionSession(configuration);
1974                     CameraExtensionSession extensionSession =
1975                             sessionListener.waitAndGetSession(
1976                                     SESSION_CONFIGURE_TIMEOUT_MS);
1977                     assertNotNull(extensionSession);
1978 
1979                     CaptureRequest.Builder captureBuilder =
1980                             mTestRule.getCamera().createCaptureRequest(
1981                                     android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW);
1982                     captureBuilder.addTarget(imageReaderSurface);
1983                     CameraExtensionSession.ExtensionCaptureCallback repeatingCallbackMock =
1984                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
1985                     SimpleCaptureCallback repeatingCaptureCallback =
1986                             new SimpleCaptureCallback(repeatingCallbackMock,
1987                                     extensionChars.getAvailableCaptureResultKeys(extension),
1988                                     mCollector);
1989                     CaptureRequest repeatingRequest = captureBuilder.build();
1990                     try {
1991                         extensionSession.setRepeatingRequest(repeatingRequest,
1992                                 new HandlerExecutor(mTestRule.getHandler()),
1993                                 repeatingCaptureCallback);
1994                         fail("should get IllegalArgumentException due to illegal repeating request"
1995                                 + " output target");
1996                     } catch (IllegalArgumentException e) {
1997                         // Expected, we can proceed further
1998                     }
1999 
2000                     extensionSession.close();
2001 
2002                     sessionListener.getStateWaiter().waitForState(
2003                             BlockingExtensionSessionCallback.SESSION_CLOSED,
2004                             SESSION_CLOSE_TIMEOUT_MS);
2005 
2006                     texturedSurface.release();
2007                     extensionImageReader.close();
2008 
2009                     captureBuilder = mTestRule.getCamera().createCaptureRequest(
2010                             CameraDevice.TEMPLATE_PREVIEW);
2011                     captureBuilder.addTarget(texturedSurface);
2012                     CameraExtensionSession.ExtensionCaptureCallback captureCallback =
2013                             mock(CameraExtensionSession.ExtensionCaptureCallback.class);
2014 
2015                     CaptureRequest captureRequest = captureBuilder.build();
2016                     try {
2017                         extensionSession.setRepeatingRequest(captureRequest,
2018                                 new HandlerExecutor(mTestRule.getHandler()), captureCallback);
2019                         fail("should get IllegalStateException due to closed session");
2020                     } catch (IllegalStateException e) {
2021                         // Expected, we can proceed further
2022                     }
2023 
2024                     try {
2025                         extensionSession.stopRepeating();
2026                         fail("should get IllegalStateException due to closed session");
2027                     } catch (IllegalStateException e) {
2028                         // Expected, we can proceed further
2029                     }
2030 
2031                     try {
2032                         extensionSession.capture(captureRequest,
2033                                 new HandlerExecutor(mTestRule.getHandler()), captureCallback);
2034                         fail("should get IllegalStateException due to closed session");
2035                     } catch (IllegalStateException e) {
2036                         // Expected, we can proceed further
2037                     }
2038                 } finally {
2039                     mTestRule.closeDevice(id);
2040                 }
2041             }
2042         }
2043     }
2044 }
2045