blob: c35e8d5a3bd14cbd0ef008e6e6174188245b9d47 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.camera2.cts.testcases;
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.MandatoryStreamCombination;
import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.cts.helpers.CameraErrorCollector;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
import android.os.Handler;
import android.os.HandlerThread;
import android.test.AndroidTestCase;
import android.util.Log;
import android.view.Surface;
import android.view.WindowManager;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.ex.camera2.blocking.BlockingStateCallback;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
public class Camera2ConcurrentAndroidTestCase extends Camera2ParameterizedTestCase {
private static final String TAG = "Camera2ConcurrentAndroidTestCase";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
public static class CameraTestInfo {
public String mCameraId;
public CameraDevice mCamera;
public StaticMetadata mStaticInfo;
public MandatoryStreamCombination[] mMandatoryStreamCombinations;
public CameraCaptureSession mCameraSession;
public BlockingSessionCallback mCameraSessionListener;
public BlockingStateCallback mCameraListener;
public CameraTestInfo(String cameraId, StaticMetadata staticInfo,
MandatoryStreamCombination[] mandatoryStreamCombinations,
BlockingStateCallback cameraListener) {
mCameraId = cameraId;
mStaticInfo = staticInfo;
mMandatoryStreamCombinations = mandatoryStreamCombinations;
mCameraListener = cameraListener;
}
};
protected Set<Set<String>> mConcurrentCameraIdCombinations;
protected HashMap<String, CameraTestInfo> mCameraTestInfos;
// include both standalone camera IDs and "hidden" physical camera IDs
protected String[] mAllCameraIds;
protected HashMap<String, StaticMetadata> mAllStaticInfo;
protected Handler mHandler;
protected HandlerThread mHandlerThread;
protected CameraErrorCollector mCollector;
protected String mDebugFileNameBase;
protected WindowManager mWindowManager;
/**
* Set up the camera2 test case required environments, including CameraManager,
* HandlerThread, Camera IDs, and CameraStateCallback etc.
*/
@Override
public void setUp() throws Exception {
super.setUp();
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mCollector = new CameraErrorCollector();
File filesDir = mContext.getPackageManager().isInstantApp()
? mContext.getFilesDir()
: mContext.getExternalFilesDir(null);
mDebugFileNameBase = filesDir.getPath();
mAllStaticInfo = new HashMap<String, StaticMetadata>();
List<String> hiddenPhysicalIds = new ArrayList<>();
for (String cameraId : mCameraIdsUnderTest) {
CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
StaticMetadata staticMetadata = new StaticMetadata(props,
CheckLevel.ASSERT, /*collector*/null);
mAllStaticInfo.put(cameraId, staticMetadata);
for (String physicalId : props.getPhysicalCameraIds()) {
if (!Arrays.asList(mCameraIdsUnderTest).contains(physicalId) &&
!hiddenPhysicalIds.contains(physicalId)) {
hiddenPhysicalIds.add(physicalId);
props = mCameraManager.getCameraCharacteristics(physicalId);
staticMetadata = new StaticMetadata(
mCameraManager.getCameraCharacteristics(physicalId),
CheckLevel.ASSERT, /*collector*/null);
mAllStaticInfo.put(physicalId, staticMetadata);
}
}
}
mConcurrentCameraIdCombinations =
CameraTestUtils.getConcurrentCameraIds(mCameraManager, mAdoptShellPerm);
assertNotNull("Unable to get concurrent camera combinations",
mConcurrentCameraIdCombinations);
mCameraTestInfos = new HashMap<String, CameraTestInfo>();
for (Set<String> cameraIdComb : mConcurrentCameraIdCombinations) {
for (String cameraId : cameraIdComb) {
if (!mCameraTestInfos.containsKey(cameraId)) {
StaticMetadata staticMetadata = mAllStaticInfo.get(cameraId);
assertTrue("camera id" + cameraId + "'s metadata not found in mAllStaticInfo",
staticMetadata != null);
CameraCharacteristics.Key<MandatoryStreamCombination[]> mandatoryStreamsKey =
CameraCharacteristics.SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
MandatoryStreamCombination[] combinations =
staticMetadata.getCharacteristics().get(mandatoryStreamsKey);
assertTrue("Concurrent streaming camera id " + cameraId +
" MUST have mandatory stream combinations",
(combinations != null) && (combinations.length > 0));
mCameraTestInfos.put(cameraId,
new CameraTestInfo(cameraId, staticMetadata, combinations,
new BlockingStateCallback()));
}
}
}
mAllCameraIds = new String[mCameraIdsUnderTest.length + hiddenPhysicalIds.size()];
System.arraycopy(mCameraIdsUnderTest, 0, mAllCameraIds, 0, mCameraIdsUnderTest.length);
for (int i = 0; i < hiddenPhysicalIds.size(); i++) {
mAllCameraIds[mCameraIdsUnderTest.length + i] = hiddenPhysicalIds.get(i);
}
}
@Override
public void tearDown() throws Exception {
try {
if (mHandlerThread != null) {
mHandlerThread.quitSafely();
}
mHandler = null;
if (mCollector != null) {
mCollector.verify();
}
} catch (Throwable e) {
// When new Exception(e) is used, exception info will be printed twice.
throw new Exception(e.getMessage());
} finally {
super.tearDown();
}
}
/**
* Start capture with given {@link #CaptureRequest}.
*
* @param request The {@link #CaptureRequest} to be captured.
* @param repeating If the capture is single capture or repeating.
* @param listener The {@link #CaptureCallback} camera device used to notify callbacks.
* @param handler The handler camera device used to post callbacks.
*/
protected void startCapture(String cameraId, CaptureRequest request, boolean repeating,
CaptureCallback listener, Handler handler) throws Exception {
if (VERBOSE) Log.v(TAG, "Starting capture from device");
CameraTestInfo info = mCameraTestInfos.get(cameraId);
assertTrue("CameraTestInfo not found for camera id " + cameraId, info != null);
if (repeating) {
info.mCameraSession.setRepeatingRequest(request, listener, handler);
} else {
info.mCameraSession.capture(request, listener, handler);
}
}
/**
* Stop the current active capture.
*
* @param fast When it is true, {@link CameraDevice#flush} is called, the stop capture
* could be faster.
*/
protected void stopCapture(String cameraId, boolean fast) throws Exception {
if (VERBOSE) Log.v(TAG, "Stopping capture");
CameraTestInfo info = mCameraTestInfos.get(cameraId);
assertTrue("CameraTest info not found for camera id " + cameraId, info != null);
if (fast) {
/**
* Flush is useful for canceling long exposure single capture, it also could help
* to make the streaming capture stop sooner.
*/
info.mCameraSession.abortCaptures();
info.mCameraSessionListener.getStateWaiter().
waitForState(BlockingSessionCallback.SESSION_READY, CAMERA_IDLE_TIMEOUT_MS);
} else {
info.mCameraSession.close();
info.mCameraSessionListener.getStateWaiter().
waitForState(BlockingSessionCallback.SESSION_CLOSED, CAMERA_IDLE_TIMEOUT_MS);
}
}
/**
* Open a {@link #CameraDevice camera device} and get the StaticMetadata for a given camera id.
* The default mCameraListener is used to wait for states.
*
* @param cameraId The id of the camera device to be opened.
*/
protected void openDevice(String cameraId) throws Exception {
CameraTestInfo info = mCameraTestInfos.get(cameraId);
assertTrue("CameraTest info not found for camera id " + cameraId, info != null);
openDevice(cameraId, info.mCameraListener);
}
/**
* Open a {@link #CameraDevice} and get the StaticMetadata for a given camera id and listener.
*
* @param cameraId The id of the camera device to be opened.
* @param listener The {@link #BlockingStateCallback} used to wait for states.
*/
protected void openDevice(String cameraId, BlockingStateCallback listener) throws Exception {
CameraTestInfo info = mCameraTestInfos.get(cameraId);
assertTrue("CameraTest info not found for camera id " + cameraId, info != null);
info.mCamera = CameraTestUtils.openCamera(
mCameraManager, cameraId, listener, mHandler);
mCollector.setCameraId(cameraId);
if (VERBOSE) {
Log.v(TAG, "Camera " + cameraId + " is opened");
}
}
/**
* Create a {@link #CameraCaptureSession} using the currently open camera with
* OutputConfigurations.
*
* @param outputSurfaces The set of output surfaces to configure for this session
*/
protected void createSessionByConfigs(String cameraId,
List<OutputConfiguration> outputConfigs) throws Exception {
CameraTestInfo info = mCameraTestInfos.get(cameraId);
assertTrue("CameraTest info not found for camera id " + cameraId, info != null);
info.mCameraSessionListener = new BlockingSessionCallback();
info.mCameraSession = CameraTestUtils.configureCameraSessionWithConfig(info.mCamera,
outputConfigs, info.mCameraSessionListener, mHandler);
}
/**
* Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
* given camera id. The default mCameraListener is used to wait for states.
* <p>
* This function must be used along with the {@link #openDevice} for the
* same camera id.
* </p>
*
* @param cameraId The id of the {@link #CameraDevice camera device} to be closed.
*/
protected void closeDevice(String cameraId) {
CameraTestInfo info = mCameraTestInfos.get(cameraId);
assertTrue("CameraTest info not found for camera id " + cameraId, info != null);
closeDevice(cameraId, info.mCameraListener);
}
/**
* Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
* given camera id and listener.
* <p>
* This function must be used along with the {@link #openDevice} for the
* same camera id.
* </p>
*
* @param cameraId The id of the camera device to be closed.
* @param listener The BlockingStateCallback used to wait for states.
*/
protected void closeDevice(String cameraId, BlockingStateCallback listener) {
CameraTestInfo info = mCameraTestInfos.get(cameraId);
assertTrue("CameraTest info not found for camera id " + cameraId, info != null);
if (info.mCamera != null) {
info.mCamera.close();
listener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
info.mCamera = null;
info.mCameraSession = null;
info.mCameraSessionListener = null;
if (VERBOSE) {
Log.v(TAG, "Camera " + cameraId + " is closed");
}
}
}
}