Merge "camera2: validate reprocess request's session"
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 31e6e25..0cf8df1 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -65,6 +65,12 @@
public abstract class CameraCaptureSession implements AutoCloseable {
/**
+ * Used to identify invalid session ID.
+ * @hide
+ */
+ public static final int SESSION_ID_NONE = -1;
+
+ /**
* Get the camera device that this session is created for.
*/
public abstract CameraDevice getDevice();
@@ -168,10 +174,11 @@
* @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
* configured as outputs for this session; or a reprocess
* capture request is submitted in a non-reprocessible capture
- * session; or the capture targets a Surface in the middle
- * of being {@link #prepare prepared}; or the handler is
- * null, the listener is not null, and the calling thread has
- * no looper.
+ * session; or the reprocess capture request was created with
+ * a {@link TotalCaptureResult} from a different session; or
+ * the capture targets a Surface in the middle of being
+ * {@link #prepare prepared}; or the handler is null, the
+ * listener is not null, and the calling thread has no looper.
*
* @see #captureBurst
* @see #setRepeatingRequest
@@ -226,7 +233,9 @@
* capture request is submitted in a non-reprocessible capture
* session; or the list of requests contains both requests to
* capture images from the camera and reprocess capture
- * requests; or one of the captures targets a Surface in the
+ * requests; or one of the reprocess capture requests was
+ * created with a {@link TotalCaptureResult} from a different
+ * session; or one of the captures targets a Surface in the
* middle of being {@link #prepare prepared}; or if the handler
* is null, the listener is not null, and the calling thread
* has no looper.
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 35727e8..19d17b1 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -158,6 +158,9 @@
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
private boolean mIsReprocess;
+ // Each reprocess request must be tied to a reprocessible session ID.
+ // Valid only for reprocess requests (mIsReprocess == true).
+ private int mReprocessibleSessionId;
private Object mUserTag;
@@ -170,6 +173,7 @@
mSettings = new CameraMetadataNative();
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = false;
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
/**
@@ -182,6 +186,7 @@
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
mIsReprocess = source.mIsReprocess;
+ mReprocessibleSessionId = source.mReprocessibleSessionId;
mUserTag = source.mUserTag;
}
@@ -189,11 +194,36 @@
* Take ownership of passed-in settings.
*
* Used by the Builder to create a mutable CaptureRequest.
+ *
+ * @param settings Settings for this capture request.
+ * @param isReprocess Indicates whether to create a reprocess capture request. {@code true}
+ * to create a reprocess capture request. {@code false} to create a regular
+ * capture request.
+ * @param reprocessibleSessionId The ID of the camera capture session this capture is created
+ * for. This is used to validate if the application submits a
+ * reprocess capture request to the same session where
+ * the {@link TotalCaptureResult}, used to create the reprocess
+ * capture, came from.
+ *
+ * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+ * reprocessibleSessionId.
+ *
+ * @see CameraDevice#createReprocessCaptureRequest
*/
- private CaptureRequest(CameraMetadataNative settings, boolean isReprocess) {
+ private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
+ int reprocessibleSessionId) {
mSettings = CameraMetadataNative.move(settings);
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = isReprocess;
+ if (isReprocess) {
+ if (reprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+ throw new IllegalArgumentException("Create a reprocess capture request with an " +
+ "invalid session ID: " + reprocessibleSessionId);
+ }
+ mReprocessibleSessionId = reprocessibleSessionId;
+ } else {
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
+ }
}
/**
@@ -277,6 +307,23 @@
}
/**
+ * Get the reprocessible session ID this reprocess capture request is associated with.
+ *
+ * @return the reprocessible session ID this reprocess capture request is associated with
+ *
+ * @throws IllegalStateException if this capture request is not a reprocess capture request.
+ * @hide
+ */
+ public int getReprocessibleSessionId() {
+ if (mIsReprocess == false ||
+ mReprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+ throw new IllegalStateException("Getting the reprocessible session ID for a "+
+ "non-reprocess capture request is illegal.");
+ }
+ return mReprocessibleSessionId;
+ }
+
+ /**
* Determine whether this CaptureRequest is equal to another CaptureRequest.
*
* <p>A request is considered equal to another is if it's set of key/values is equal, it's
@@ -298,7 +345,8 @@
&& Objects.equals(mUserTag, other.mUserTag)
&& mSurfaceSet.equals(other.mSurfaceSet)
&& mSettings.equals(other.mSettings)
- && mIsReprocess == other.mIsReprocess;
+ && mIsReprocess == other.mIsReprocess
+ && mReprocessibleSessionId == other.mReprocessibleSessionId;
}
@Override
@@ -347,6 +395,7 @@
}
mIsReprocess = (in.readInt() == 0) ? false : true;
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
@Override
@@ -397,10 +446,23 @@
* Initialize the builder using the template; the request takes
* ownership of the template.
*
+ * @param template Template settings for this capture request.
+ * @param reprocess Indicates whether to create a reprocess capture request. {@code true}
+ * to create a reprocess capture request. {@code false} to create a regular
+ * capture request.
+ * @param reprocessibleSessionId The ID of the camera capture session this capture is
+ * created for. This is used to validate if the application
+ * submits a reprocess capture request to the same session
+ * where the {@link TotalCaptureResult}, used to create the
+ * reprocess capture, came from.
+ *
+ * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+ * reprocessibleSessionId.
* @hide
*/
- public Builder(CameraMetadataNative template, boolean reprocess) {
- mRequest = new CaptureRequest(template, reprocess);
+ public Builder(CameraMetadataNative template, boolean reprocess,
+ int reprocessibleSessionId) {
+ mRequest = new CaptureRequest(template, reprocess, reprocessibleSessionId);
}
/**
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 6f7dd78..fb3c098 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -50,6 +50,7 @@
public final class TotalCaptureResult extends CaptureResult {
private final List<CaptureResult> mPartialResults;
+ private final int mSessionId;
/**
* Takes ownership of the passed-in camera metadata and the partial results
@@ -58,7 +59,7 @@
* @hide
*/
public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
- CaptureResultExtras extras, List<CaptureResult> partials) {
+ CaptureResultExtras extras, List<CaptureResult> partials, int sessionId) {
super(results, parent, extras);
if (partials == null) {
@@ -66,6 +67,8 @@
} else {
mPartialResults = partials;
}
+
+ mSessionId = sessionId;
}
/**
@@ -78,6 +81,7 @@
super(results, sequenceId);
mPartialResults = new ArrayList<>();
+ mSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
/**
@@ -95,4 +99,14 @@
public List<CaptureResult> getPartialResults() {
return Collections.unmodifiableList(mPartialResults);
}
+
+ /**
+ * Get the ID of the session where the capture request of this result was submitted.
+ *
+ * @return The session ID
+ * @hide
+ */
+ public int getSessionId() {
+ return mSessionId;
+ }
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index c74204d..3c19529 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -156,9 +156,10 @@
} else if (request.isReprocess() && !isReprocessible()) {
throw new IllegalArgumentException("this capture session cannot handle reprocess " +
"requests");
+ } else if (request.isReprocess() && request.getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another session");
}
-
checkNotClosed();
handler = checkHandler(handler, callback);
@@ -185,12 +186,17 @@
if (reprocess && !isReprocessible()) {
throw new IllegalArgumentException("this capture session cannot handle reprocess " +
"requests");
+ } else if (reprocess && requests.get(0).getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another session");
}
for (int i = 1; i < requests.size(); i++) {
if (requests.get(i).isReprocess() != reprocess) {
throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
" requests");
+ } else if (reprocess && requests.get(i).getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another " +
+ "session");
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1e680dfd..ff4ad79 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -585,8 +585,8 @@
return null;
}
- CaptureRequest.Builder builder =
- new CaptureRequest.Builder(templatedRequest, /*reprocess*/false);
+ CaptureRequest.Builder builder = new CaptureRequest.Builder(
+ templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
return builder;
}
@@ -601,7 +601,8 @@
CameraMetadataNative resultMetadata = new
CameraMetadataNative(inputResult.getNativeCopy());
- return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true);
+ return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
+ inputResult.getSessionId());
}
}
@@ -763,7 +764,7 @@
if (callback != null) {
mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback,
- requestList, handler, repeating));
+ requestList, handler, repeating, mNextSessionId - 1));
} else {
if (DEBUG) {
Log.d(TAG, "Listen for request " + requestId + " is null");
@@ -1095,9 +1096,10 @@
private final CaptureCallback mCallback;
private final List<CaptureRequest> mRequestList;
private final Handler mHandler;
+ private final int mSessionId;
CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
- Handler handler, boolean repeating) {
+ Handler handler, boolean repeating, int sessionId) {
if (callback == null || handler == null) {
throw new UnsupportedOperationException(
"Must have a valid handler and a valid callback");
@@ -1106,6 +1108,7 @@
mHandler = handler;
mRequestList = new ArrayList<CaptureRequest>(requestList);
mCallback = callback;
+ mSessionId = sessionId;
}
public boolean isRepeating() {
@@ -1140,6 +1143,10 @@
return mHandler;
}
+ public int getSessionId() {
+ return mSessionId;
+ }
+
}
/**
@@ -1643,8 +1650,8 @@
List<CaptureResult> partialResults =
mFrameNumberTracker.popPartialResults(frameNumber);
- final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, resultExtras, partialResults);
+ final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
+ request, resultExtras, partialResults, holder.getSessionId());
// Final capture result
resultDispatch = new Runnable() {
@@ -1665,7 +1672,8 @@
holder.getHandler().post(resultDispatch);
// Collect the partials for a total result; or mark the frame as totally completed
- mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, isReprocess);
+ mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
+ isReprocess);
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 6f33672..d71b44b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -19,6 +19,7 @@
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceCallbacks;
@@ -170,7 +171,8 @@
assertEquals(CameraBinderTestUtils.NO_ERROR, status);
assertFalse(metadata.isEmpty());
- CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false);
+ CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false,
+ CameraCaptureSession.SESSION_ID_NONE);
assertFalse(request.isEmpty());
assertFalse(metadata.isEmpty());
if (needStream) {