Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7a316f6b authored by Chien-Yu Chen's avatar Chien-Yu Chen
Browse files

camera2: validate reprocess request's session

Add a session ID to CaptureResult to indicate the session where
the result comes from. When creating a reprocess capture request
with a capture result, the session ID will be carried over to
the reprocess capture request. Reprocess capture request's session
ID will be used to validate that it matches the session ID when
submitting the reprocess capture request to a session.

Bug: 20263212
Change-Id: I024c1a28ecf0a43909a0ed3814a11360c318417f
parent 47fb5f1e
Loading
Loading
Loading
Loading
+14 −5
Original line number Diff line number Diff line
@@ -64,6 +64,12 @@ import java.util.List;
 */
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.
     */
@@ -168,10 +174,11 @@ public abstract class CameraCaptureSession implements AutoCloseable {
     * @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 @@ public abstract class CameraCaptureSession implements AutoCloseable {
     *                                  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.
+66 −4
Original line number Diff line number Diff line
@@ -158,6 +158,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
    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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
        mSettings = new CameraMetadataNative();
        mSurfaceSet = new HashSet<Surface>();
        mIsReprocess = false;
        mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
    }

    /**
@@ -182,6 +186,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
        mSettings = new CameraMetadataNative(source.mSettings);
        mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
        mIsReprocess = source.mIsReprocess;
        mReprocessibleSessionId = source.mReprocessibleSessionId;
        mUserTag = source.mUserTag;
    }

@@ -189,11 +194,36 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
     * 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;
        }
    }

    /**
@@ -276,6 +306,23 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
        return mIsReprocess;
    }

    /**
     * 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.
     *
@@ -298,7 +345,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
                && 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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
        }

        mIsReprocess = (in.readInt() == 0) ? false : true;
        mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
    }

    @Override
@@ -397,10 +446,23 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
         * 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);
        }

        /**
+15 −1
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import java.util.List;
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 @@ public final class TotalCaptureResult extends CaptureResult {
     * @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 @@ public final class TotalCaptureResult extends CaptureResult {
        } else {
            mPartialResults = partials;
        }

        mSessionId = sessionId;
    }

    /**
@@ -78,6 +81,7 @@ public final class TotalCaptureResult extends CaptureResult {
        super(results, sequenceId);

        mPartialResults = new ArrayList<>();
        mSessionId = CameraCaptureSession.SESSION_ID_NONE;
    }

    /**
@@ -95,4 +99,14 @@ public final class TotalCaptureResult extends CaptureResult {
    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;
    }
}
+7 −1
Original line number Diff line number Diff line
@@ -156,9 +156,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
        } 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 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
        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");
            }
        }

+16 −8
Original line number Diff line number Diff line
@@ -585,8 +585,8 @@ public class CameraDeviceImpl extends CameraDevice {
                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 @@ public class CameraDeviceImpl extends CameraDevice {
            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 @@ public class CameraDeviceImpl extends CameraDevice {

            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 @@ public class CameraDeviceImpl extends CameraDevice {
        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 @@ public class CameraDeviceImpl extends CameraDevice {
            mHandler = handler;
            mRequestList = new ArrayList<CaptureRequest>(requestList);
            mCallback = callback;
            mSessionId = sessionId;
        }

        public boolean isRepeating() {
@@ -1140,6 +1143,10 @@ public class CameraDeviceImpl extends CameraDevice {
            return mHandler;
        }

        public int getSessionId() {
            return mSessionId;
        }

    }

    /**
@@ -1643,8 +1650,8 @@ public class CameraDeviceImpl extends CameraDevice {
                    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 @@ public class CameraDeviceImpl extends CameraDevice {
                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) {
Loading