All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.hardware.camera2.impl.CameraOfflineSessionImpl Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 15-robolectric-12650502
Show newest version
/*
 * Copyright (C) 2019 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.impl;

import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraOfflineSession;
import android.hardware.camera2.CameraOfflineSession.CameraOfflineSessionCallback;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraOfflineSession;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Range;
import android.util.SparseArray;
import android.view.Surface;

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.Executor;

import static com.android.internal.util.Preconditions.*;

public class CameraOfflineSessionImpl extends CameraOfflineSession
        implements IBinder.DeathRecipient {
    private static final String TAG = "CameraOfflineSessionImpl";
    private static final int REQUEST_ID_NONE = -1;
    private static final long NANO_PER_SECOND = 1000000000; //ns
    private final boolean DEBUG = false;

    private ICameraOfflineSession mRemoteSession;
    private final AtomicBoolean mClosing = new AtomicBoolean();

    private SimpleEntry mOfflineInput =
            new SimpleEntry<>(REQUEST_ID_NONE, null);
    private SparseArray mOfflineOutputs = new SparseArray<>();
    private SparseArray mConfiguredOutputs = new SparseArray<>();

    final Object mInterfaceLock = new Object(); // access from this class and Session only!

    private final String mCameraId;
    private final CameraCharacteristics mCharacteristics;
    private final int mTotalPartialCount;

    private final Executor mOfflineExecutor;
    private final CameraOfflineSessionCallback mOfflineCallback;

    private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();

    /**
     * A list tracking request and its expected last regular/reprocess/zslStill frame
     * number.
     */
    private List mOfflineRequestLastFrameNumbersList =
            new ArrayList<>();

    /**
     * An object tracking received frame numbers.
     * Updated when receiving callbacks from ICameraDeviceCallbacks.
     */
    private FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();

    /** map request IDs to callback/request data */
    private SparseArray mCaptureCallbackMap =
            new SparseArray();

    public CameraOfflineSessionImpl(String cameraId, CameraCharacteristics characteristics,
            Executor offlineExecutor, CameraOfflineSessionCallback offlineCallback,
            SparseArray offlineOutputs,
            SimpleEntry offlineInput,
            SparseArray configuredOutputs,
            FrameNumberTracker frameNumberTracker, SparseArray callbackMap,
            List frameNumberList) {
        if ((cameraId == null) || (characteristics == null)) {
            throw new IllegalArgumentException("Null argument given");
        }

        mCameraId = cameraId;
        mCharacteristics = characteristics;

        Integer partialCount =
                mCharacteristics.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT);
        if (partialCount == null) {
            // 1 means partial result is not supported.
            mTotalPartialCount = 1;
        } else {
            mTotalPartialCount = partialCount;
        }

        mOfflineRequestLastFrameNumbersList.addAll(frameNumberList);
        mFrameNumberTracker = frameNumberTracker;
        mCaptureCallbackMap = callbackMap;
        mConfiguredOutputs = configuredOutputs;
        mOfflineOutputs = offlineOutputs;
        mOfflineInput = offlineInput;
        mOfflineExecutor = checkNotNull(offlineExecutor, "offline executor must not be null");
        mOfflineCallback = checkNotNull(offlineCallback, "offline callback must not be null");

    }

    public CameraDeviceCallbacks getCallbacks() {
        return mCallbacks;
    }

    public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
        @Override
        public IBinder asBinder() {
            return this;
        }

        @Override
        public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
            synchronized(mInterfaceLock) {

                switch (errorCode) {
                    case CameraDeviceCallbacks.ERROR_CAMERA_REQUEST:
                    case CameraDeviceCallbacks.ERROR_CAMERA_RESULT:
                    case CameraDeviceCallbacks.ERROR_CAMERA_BUFFER:
                        onCaptureErrorLocked(errorCode, resultExtras);
                        break;
                    default: {
                        Runnable errorDispatch = new Runnable() {
                            @Override
                            public void run() {
                                if (!isClosed()) {
                                    mOfflineCallback.onError(CameraOfflineSessionImpl.this,
                                            CameraOfflineSessionCallback.STATUS_INTERNAL_ERROR);
                                }
                            }
                        };

                        final long ident = Binder.clearCallingIdentity();
                        try {
                            mOfflineExecutor.execute(errorDispatch);
                        } finally {
                            Binder.restoreCallingIdentity(ident);
                        }
                    }
                }
            }
        }

        @Override
        public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
            Log.e(TAG, "Unexpected repeating request error received. Last frame number is " +
                    lastFrameNumber);
        }

        @Override
        public void onDeviceIdle() {
            synchronized(mInterfaceLock) {
                if (mRemoteSession == null) {
                    Log.v(TAG, "Ignoring idle state notifications during offline switches");
                    return;
                }

                // Remove all capture callbacks now that device has gone to IDLE state.
                removeCompletedCallbackHolderLocked(
                        Long.MAX_VALUE, /*lastCompletedRegularFrameNumber*/
                        Long.MAX_VALUE, /*lastCompletedReprocessFrameNumber*/
                        Long.MAX_VALUE /*lastCompletedZslStillFrameNumber*/);

                Runnable idleDispatch = new Runnable() {
                    @Override
                    public void run() {
                        if (!isClosed()) {
                            mOfflineCallback.onIdle(CameraOfflineSessionImpl.this);
                        }
                    }
                };

                final long ident = Binder.clearCallingIdentity();
                try {
                    mOfflineExecutor.execute(idleDispatch);
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
        }

        @Override
        public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
            int requestId = resultExtras.getRequestId();
            final long frameNumber = resultExtras.getFrameNumber();
            final long lastCompletedRegularFrameNumber =
                    resultExtras.getLastCompletedRegularFrameNumber();
            final long lastCompletedReprocessFrameNumber =
                    resultExtras.getLastCompletedReprocessFrameNumber();
            final long lastCompletedZslFrameNumber =
                    resultExtras.getLastCompletedZslFrameNumber();

            final CaptureCallbackHolder holder;

            synchronized(mInterfaceLock) {
                // Check if it's okay to remove completed callbacks from mCaptureCallbackMap.
                // A callback is completed if the corresponding inflight request has been removed
                // from the inflight queue in cameraservice.
                removeCompletedCallbackHolderLocked(lastCompletedRegularFrameNumber,
                        lastCompletedReprocessFrameNumber, lastCompletedZslFrameNumber);

                // Get the callback for this frame ID, if there is one
                holder = CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId);

                if (holder == null) {
                    return;
                }

                final Executor executor = holder.getCallback().getExecutor();
                if (isClosed() || (executor == null)) return;

                // Dispatch capture start notice
                final long ident = Binder.clearCallingIdentity();
                try {
                    executor.execute(
                        new Runnable() {
                            @Override
                            public void run() {
                                final CameraCaptureSession.CaptureCallback callback =
                                        holder.getCallback().getSessionCallback();
                                if (!CameraOfflineSessionImpl.this.isClosed() &&
                                        (callback != null)) {
                                    final int subsequenceId = resultExtras.getSubsequenceId();
                                    final CaptureRequest request = holder.getRequest(subsequenceId);

                                    if (holder.hasBatchedOutputs()) {
                                        // Send derived onCaptureStarted for requests within the
                                        // batch
                                        final Range fpsRange =
                                                request.get(
                                                        CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
                                        for (int i = 0; i < holder.getRequestCount(); i++) {
                                            final CaptureRequest cbRequest = holder.getRequest(i);
                                            final long cbTimestamp =
                                                        timestamp - (subsequenceId - i) *
                                                        NANO_PER_SECOND/fpsRange.getUpper();
                                            final long cbFrameNumber =
                                                    frameNumber - (subsequenceId - i);
                                            callback.onCaptureStarted(CameraOfflineSessionImpl.this,
                                                    cbRequest, cbTimestamp, cbFrameNumber);
                                        }
                                    } else {
                                        callback.onCaptureStarted(CameraOfflineSessionImpl.this,
                                                holder.getRequest(
                                                    resultExtras.getSubsequenceId()),
                                                timestamp, frameNumber);
                                    }
                                }
                            }
                        });
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
        }

        @Override
        public void onResultReceived(CameraMetadataNative result,
                CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])
                throws RemoteException {

            int requestId = resultExtras.getRequestId();
            long frameNumber = resultExtras.getFrameNumber();

            synchronized(mInterfaceLock) {
                // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
                result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
                        mCharacteristics.get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));

                final CaptureCallbackHolder holder =
                        CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId);
                final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());

                boolean isPartialResult =
                        (resultExtras.getPartialResultCount() < mTotalPartialCount);
                int requestType = request.getRequestType();

                // Check if we have a callback for this
                if (holder == null) {
                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
                            requestType);

                    return;
                }

                if (isClosed()) {
                    mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
                            requestType);
                    return;
                }


                Runnable resultDispatch = null;

                CaptureResult finalResult;
                // Make a copy of the native metadata before it gets moved to a CaptureResult
                // object.
                final CameraMetadataNative resultCopy;
                if (holder.hasBatchedOutputs()) {
                    resultCopy = new CameraMetadataNative(result);
                } else {
                    resultCopy = null;
                }

                final Executor executor = holder.getCallback().getExecutor();
                // Either send a partial result or the final capture completed result
                if (isPartialResult) {
                    final CaptureResult resultAsCapture =
                            new CaptureResult(mCameraId, result, request, resultExtras);
                    // Partial result
                    resultDispatch = new Runnable() {
                        @Override
                        public void run() {
                            final CameraCaptureSession.CaptureCallback callback =
                                    holder.getCallback().getSessionCallback();
                            if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) {
                                if (holder.hasBatchedOutputs()) {
                                    // Send derived onCaptureProgressed for requests within
                                    // the batch.
                                    for (int i = 0; i < holder.getRequestCount(); i++) {
                                        CameraMetadataNative resultLocal =
                                                new CameraMetadataNative(resultCopy);
                                        final CaptureResult resultInBatch = new CaptureResult(
                                                mCameraId, resultLocal, holder.getRequest(i),
                                                resultExtras);

                                        final CaptureRequest cbRequest = holder.getRequest(i);
                                        callback.onCaptureProgressed(CameraOfflineSessionImpl.this,
                                                cbRequest, resultInBatch);
                                    }
                                } else {
                                    callback.onCaptureProgressed(CameraOfflineSessionImpl.this,
                                            request, resultAsCapture);
                                }
                            }
                        }
                    };
                    finalResult = resultAsCapture;
                } else {
                    List partialResults =
                            mFrameNumberTracker.popPartialResults(frameNumber);

                    final long sensorTimestamp =
                            result.get(CaptureResult.SENSOR_TIMESTAMP);
                    final Range fpsRange =
                            request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
                    final int subsequenceId = resultExtras.getSubsequenceId();
                    final TotalCaptureResult resultAsCapture = new TotalCaptureResult(mCameraId,
                            result, request, resultExtras, partialResults, holder.getSessionId(),
                            physicalResults);
                    // Final capture result
                    resultDispatch = new Runnable() {
                        @Override
                        public void run() {
                            final CameraCaptureSession.CaptureCallback callback =
                                    holder.getCallback().getSessionCallback();
                            if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) {
                                if (holder.hasBatchedOutputs()) {
                                    // Send derived onCaptureCompleted for requests within
                                    // the batch.
                                    for (int i = 0; i < holder.getRequestCount(); i++) {
                                        resultCopy.set(CaptureResult.SENSOR_TIMESTAMP,
                                                sensorTimestamp - (subsequenceId - i) *
                                                NANO_PER_SECOND/fpsRange.getUpper());
                                        CameraMetadataNative resultLocal =
                                                new CameraMetadataNative(resultCopy);
                                        // No logical multi-camera support for batched output mode.
                                        TotalCaptureResult resultInBatch = new TotalCaptureResult(
                                                mCameraId, resultLocal, holder.getRequest(i),
                                                resultExtras, partialResults, holder.getSessionId(),
                                                new PhysicalCaptureResultInfo[0]);

                                        final CaptureRequest cbRequest = holder.getRequest(i);
                                        callback.onCaptureCompleted(CameraOfflineSessionImpl.this,
                                                cbRequest, resultInBatch);
                                    }
                                } else {
                                    callback.onCaptureCompleted(CameraOfflineSessionImpl.this,
                                            request, resultAsCapture);
                                }
                            }
                        }
                    };
                    finalResult = resultAsCapture;
                }

                if (executor != null) {
                    final long ident = Binder.clearCallingIdentity();
                    try {
                        executor.execute(resultDispatch);
                    } finally {
                        Binder.restoreCallingIdentity(ident);
                    }
                }

                // Collect the partials for a total result; or mark the frame as totally completed
                mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
                        requestType);

                // Fire onCaptureSequenceCompleted
                if (!isPartialResult) {
                    checkAndFireSequenceComplete();
                }
            }
        }

        @Override
        public void onPrepared(int streamId) {
            Log.e(TAG, "Unexpected stream " + streamId + " is prepared");
        }

        @Override
        public void onRequestQueueEmpty() {
            // No-op during offline mode
            Log.v(TAG, "onRequestQueueEmpty");
        }

        /**
         * Called by onDeviceError for handling single-capture failures.
         */
        private void onCaptureErrorLocked(int errorCode, CaptureResultExtras resultExtras) {
            final int requestId = resultExtras.getRequestId();
            final int subsequenceId = resultExtras.getSubsequenceId();
            final long frameNumber = resultExtras.getFrameNumber();
            final String errorPhysicalCameraId = resultExtras.getErrorPhysicalCameraId();
            final CaptureCallbackHolder holder =
                    CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId);

            if (holder == null) {
                Log.e(TAG, String.format("Receive capture error on unknown request ID %d",
                        requestId));
                return;
            }

            final CaptureRequest request = holder.getRequest(subsequenceId);

            Runnable failureDispatch = null;
            if (errorCode == ERROR_CAMERA_BUFFER) {
                // Because 1 stream id could map to multiple surfaces, we need to specify both
                // streamId and surfaceId.
                OutputConfiguration config;
                if ((mRemoteSession == null) && !isClosed()) {
                    config = mConfiguredOutputs.get(resultExtras.getErrorStreamId());
                } else {
                    config = mOfflineOutputs.get(resultExtras.getErrorStreamId());
                }
                if (config == null) {
                    Log.v(TAG, String.format(
                            "Stream %d has been removed. Skipping buffer lost callback",
                            resultExtras.getErrorStreamId()));
                    return;
                }
                for (Surface surface : config.getSurfaces()) {
                    if (!request.containsTarget(surface)) {
                        continue;
                    }
                    final Executor executor = holder.getCallback().getExecutor();
                    failureDispatch = new Runnable() {
                        @Override
                        public void run() {
                            final CameraCaptureSession.CaptureCallback callback =
                                    holder.getCallback().getSessionCallback();
                            if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) {
                                callback.onCaptureBufferLost( CameraOfflineSessionImpl.this,
                                        request, surface, frameNumber);
                            }
                        }
                    };
                    if (executor != null) {
                        // Dispatch the failure callback
                        final long ident = Binder.clearCallingIdentity();
                        try {
                            executor.execute(failureDispatch);
                        } finally {
                            Binder.restoreCallingIdentity(ident);
                        }
                    }
                }
            } else {
                boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
                int reason = CaptureFailure.REASON_ERROR;

                final CaptureFailure failure = new CaptureFailure(
                    request,
                    reason,
                    /*dropped*/ mayHaveBuffers,
                    requestId,
                    frameNumber,
                    errorPhysicalCameraId);

                final Executor executor = holder.getCallback().getExecutor();
                failureDispatch = new Runnable() {
                    @Override
                    public void run() {
                        final CameraCaptureSession.CaptureCallback callback =
                                holder.getCallback().getSessionCallback();
                        if (!CameraOfflineSessionImpl.this.isClosed() && (callback != null)) {
                            callback.onCaptureFailed(CameraOfflineSessionImpl.this, request,
                                    failure);
                        }
                    }
                };

                // Fire onCaptureSequenceCompleted if appropriate
                mFrameNumberTracker.updateTracker(frameNumber,
                        /*error*/true, request.getRequestType());
                checkAndFireSequenceComplete();

                if (executor != null) {
                    // Dispatch the failure callback
                    final long ident = Binder.clearCallingIdentity();
                    try {
                        executor.execute(failureDispatch);
                    } finally {
                        Binder.restoreCallingIdentity(ident);
                    }
                }
            }

        }

    }

    private void checkAndFireSequenceComplete() {
        long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
        long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
        long completedZslStillFrameNumber = mFrameNumberTracker.getCompletedZslStillFrameNumber();
        Iterator iter =
                mOfflineRequestLastFrameNumbersList.iterator();
        while (iter.hasNext()) {
            final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
            boolean sequenceCompleted = false;
            final int requestId = requestLastFrameNumbers.getRequestId();
            final CaptureCallbackHolder holder;
            final Executor executor;
            final CameraCaptureSession.CaptureCallback callback;
            synchronized(mInterfaceLock) {
                int index = mCaptureCallbackMap.indexOfKey(requestId);
                holder = (index >= 0) ?
                        mCaptureCallbackMap.valueAt(index) : null;
                if (holder != null) {
                    long lastRegularFrameNumber =
                            requestLastFrameNumbers.getLastRegularFrameNumber();
                    long lastReprocessFrameNumber =
                            requestLastFrameNumbers.getLastReprocessFrameNumber();
                    long lastZslStillFrameNumber =
                            requestLastFrameNumbers.getLastZslStillFrameNumber();
                    executor = holder.getCallback().getExecutor();
                    callback = holder.getCallback().getSessionCallback();
                    // check if it's okay to remove request from mCaptureCallbackMap
                    if (lastRegularFrameNumber <= completedFrameNumber
                            && lastReprocessFrameNumber <= completedReprocessFrameNumber
                            && lastZslStillFrameNumber <= completedZslStillFrameNumber) {
                        sequenceCompleted = true;
                        mCaptureCallbackMap.removeAt(index);
                    }
                } else {
                    executor = null;
                    callback = null;
                }
            }

            // If no callback is registered for this requestId or sequence completed, remove it
            // from the frame number->request pair because it's not needed anymore.
            if (holder == null || sequenceCompleted) {
                iter.remove();
            }

            // Call onCaptureSequenceCompleted
            if ((sequenceCompleted) && (callback != null) && (executor != null)) {
                Runnable resultDispatch = new Runnable() {
                    @Override
                    public void run() {
                        if (!isClosed()) {
                            callback.onCaptureSequenceCompleted(CameraOfflineSessionImpl.this,
                                    requestId, requestLastFrameNumbers.getLastFrameNumber());
                        }
                    }
                };

                final long ident = Binder.clearCallingIdentity();
                try {
                    executor.execute(resultDispatch);
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }

                if (mCaptureCallbackMap.size() == 0) {
                    getCallbacks().onDeviceIdle();
                }
            }

        }
    }

    private void removeCompletedCallbackHolderLocked(long lastCompletedRegularFrameNumber,
            long lastCompletedReprocessFrameNumber, long lastCompletedZslStillFrameNumber) {
        if (DEBUG) {
            Log.v(TAG, String.format("remove completed callback holders for "
                    + "lastCompletedRegularFrameNumber %d, "
                    + "lastCompletedReprocessFrameNumber %d, "
                    + "lastCompletedZslStillFrameNumber %d",
                    lastCompletedRegularFrameNumber,
                    lastCompletedReprocessFrameNumber,
                    lastCompletedZslStillFrameNumber));
        }

        boolean isReprocess = false;
        Iterator iter =
                mOfflineRequestLastFrameNumbersList.iterator();
        while (iter.hasNext()) {
            final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
            final int requestId = requestLastFrameNumbers.getRequestId();
            final CaptureCallbackHolder holder;

            int index = mCaptureCallbackMap.indexOfKey(requestId);
            holder = (index >= 0) ?
                    mCaptureCallbackMap.valueAt(index) : null;
            if (holder != null) {
                long lastRegularFrameNumber =
                        requestLastFrameNumbers.getLastRegularFrameNumber();
                long lastReprocessFrameNumber =
                        requestLastFrameNumbers.getLastReprocessFrameNumber();
                long lastZslStillFrameNumber =
                        requestLastFrameNumbers.getLastZslStillFrameNumber();
                if (lastRegularFrameNumber <= lastCompletedRegularFrameNumber
                        && lastReprocessFrameNumber <= lastCompletedReprocessFrameNumber
                        && lastZslStillFrameNumber <= lastCompletedZslStillFrameNumber) {
                    if (requestLastFrameNumbers.isSequenceCompleted()) {
                        mCaptureCallbackMap.removeAt(index);
                        if (DEBUG) {
                            Log.v(TAG, String.format(
                                    "Remove holder for requestId %d, because lastRegularFrame %d "
                                    + "is <= %d, lastReprocessFrame %d is <= %d, "
                                    + "lastZslStillFrame %d is <= %d", requestId,
                                    lastRegularFrameNumber, lastCompletedRegularFrameNumber,
                                    lastReprocessFrameNumber, lastCompletedReprocessFrameNumber,
                                    lastZslStillFrameNumber, lastCompletedZslStillFrameNumber));
                        }

                        iter.remove();
                    } else {
                        Log.e(TAG, "Sequence not yet completed for request id " + requestId);
                        continue;
                    }
                }
            }
        }
    }

    public void notifyFailedSwitch() {
        synchronized(mInterfaceLock) {
            Runnable switchFailDispatch = new Runnable() {
                @Override
                public void run() {
                    mOfflineCallback.onSwitchFailed(CameraOfflineSessionImpl.this);
                }
            };

            final long ident = Binder.clearCallingIdentity();
            try {
                mOfflineExecutor.execute(switchFailDispatch);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

    /**
     * Set remote session.
     *
     */
    public void setRemoteSession(ICameraOfflineSession remoteSession) throws CameraAccessException {
        synchronized(mInterfaceLock) {
            if (remoteSession == null) {
                notifyFailedSwitch();
                return;
            }

            mRemoteSession = remoteSession;

            IBinder remoteSessionBinder = remoteSession.asBinder();
            if (remoteSessionBinder == null) {
                throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
                        "The camera offline session has encountered a serious error");
            }

            try {
                remoteSessionBinder.linkToDeath(this, /*flag*/ 0);
            } catch (RemoteException e) {
                throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
                        "The camera offline session has encountered a serious error");
            }

            Runnable readyDispatch = new Runnable() {
                @Override
                public void run() {
                    if (!isClosed()) {
                        mOfflineCallback.onReady(CameraOfflineSessionImpl.this);
                    }
                }
            };

            final long ident = Binder.clearCallingIdentity();
            try {
                mOfflineExecutor.execute(readyDispatch);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

    /** Whether the offline session has started to close (may not yet have finished) */
    private boolean isClosed() {
        return mClosing.get();
    }

    private void disconnect() {
        synchronized (mInterfaceLock) {
            if (mClosing.getAndSet(true)) {
                return;
            }

            if (mRemoteSession != null) {
                mRemoteSession.asBinder().unlinkToDeath(this, /*flags*/0);

                try {
                    mRemoteSession.disconnect();
                } catch (RemoteException e) {
                    Log.e(TAG, "Exception while disconnecting from offline session: ", e);
                }
            } else {
                throw new IllegalStateException("Offline session is not yet ready");
            }

            mRemoteSession = null;

            Runnable closeDispatch = new Runnable() {
                @Override
                public void run() {
                    mOfflineCallback.onClosed(CameraOfflineSessionImpl.this);
                }
            };

            final long ident = Binder.clearCallingIdentity();
            try {
                mOfflineExecutor.execute(closeDispatch);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            disconnect();
        }
        finally {
            super.finalize();
        }
    }

    /**
     * Listener for binder death.
     *
     * 

Handle binder death for ICameraOfflineSession.

*/ @Override public void binderDied() { Log.w(TAG, "CameraOfflineSession on device " + mCameraId + " died unexpectedly"); disconnect(); } @Override public CameraDevice getDevice() { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public void prepare(Surface surface) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public void prepare(int maxCount, Surface surface) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public void tearDown(Surface surface) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public void finalizeOutputConfigurations( List outputConfigs) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public int capture(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public int captureSingleRequest(CaptureRequest request, Executor executor, CaptureCallback callback) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public int captureBurst(List requests, CaptureCallback callback, Handler handler) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public int captureBurstRequests(List requests, Executor executor, CaptureCallback callback) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, Handler handler) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public int setSingleRepeatingRequest(CaptureRequest request, Executor executor, CaptureCallback callback) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public int setRepeatingBurst(List requests, CaptureCallback callback, Handler handler) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public int setRepeatingBurstRequests(List requests, Executor executor, CaptureCallback callback) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public void stopRepeating() throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public void abortCaptures() throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public void updateOutputConfiguration(OutputConfiguration config) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public boolean isReprocessable() { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public Surface getInputSurface() { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public CameraOfflineSession switchToOffline(Collection offlineOutputs, Executor executor, CameraOfflineSessionCallback listener) throws CameraAccessException { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public boolean supportsOfflineProcessing(Surface surface) { throw new UnsupportedOperationException("Operation not supported in offline mode"); } @Override public void close() { disconnect(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy