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

Commit ad45fa64 authored by Eino-Ville Talvala's avatar Eino-Ville Talvala Committed by Android (Google) Code Review
Browse files

Merge "Camera2: Fire all callbacks" into klp-dev

parents d0f379c1 868d9043
Loading
Loading
Loading
Loading
+42 −43
Original line number Diff line number Diff line
@@ -197,26 +197,33 @@ public interface CameraDevice extends AutoCloseable {
     * if the format is user-visible, it must be one of android.scaler.availableFormats;
     * and the size must be one of android.scaler.available[Processed|Jpeg]Sizes).</p>
     *
     * <p>To change the output, the camera device must be idle. The device is considered
     * to be idle once all in-flight and pending capture requests have been processed,
     * and all output image buffers from the captures have been sent to their destination
     * Surfaces.</p>
     *
     * <p>To reach an idle state without cancelling any submitted captures, first
     * stop any repeating request/burst with {@link #stopRepeating}, and then
     * wait for the {@link StateListener#onIdle} callback to be
     * called. To idle as fast as possible, use {@link #flush} and wait for the
     * idle callback.</p>
     * <p>When this method is called with valid Surfaces, the device will transition to the {@link
     * StateListener#onBusy busy state}. Once configuration is complete, the device will transition
     * into the {@link StateListener#onIdle idle state}. Capture requests using the newly-configured
     * Surfaces may then be submitted with {@link #capture}, {@link #captureBurst}, {@link
     * #setRepeatingRequest}, or {@link #setRepeatingBurst}.</p>
     *
     * <p>If this method is called while the camera device is still actively processing previously
     * submitted captures, then the following sequence of events occurs: The device transitions to
     * the busy state and calls the {@link StateListener#onBusy} callback. Second, if a repeating
     * request is set it is cleared.  Third, the device finishes up all in-flight and pending
     * requests. Finally, once the device is idle, it then reconfigures its outputs, and calls the
     * {@link StateListener#onIdle} method once it is again ready to accept capture
     * requests. Therefore, no submitted work is discarded. To idle as fast as possible, use {@link
     * #flush} and wait for the idle callback before calling configureOutputs. This will discard
     * work, but reaches the new configuration sooner.</p>
     *
     * <p>Using larger resolution outputs, or more outputs, can result in slower
     * output rate from the device.</p>
     *
     * <p>Configuring the outputs with an empty or null list will transition
     * the camera into an {@link StateListener#onUnconfigured unconfigured state}.
     * </p>
     * <p>Configuring the outputs with an empty or null list will transition the camera into an
     * {@link StateListener#onUnconfigured unconfigured state} instead of the {@link
     * StateListener#onIdle idle state}.  </p>
     *
     * <p>Calling configureOutputs with the same arguments as the last call to
     * configureOutputs has no effect.</p>
     * configureOutputs has no effect, and the {@link StateListener#onBusy busy}
     * and {@link StateListener#onIdle idle} state transitions will happen
     * immediately.</p>
     *
     * @param outputs The new set of Surfaces that should be made available as
     * targets for captured image data.
@@ -228,7 +235,10 @@ public interface CameraDevice extends AutoCloseable {
     * @throws IllegalStateException if the camera device is not idle, or
     *                               if the camera device has been closed
     *
     * @see StateListener#onBusy
     * @see StateListener#onIdle
     * @see StateListener#onActive
     * @see StateListener#onUnconfigured
     * @see #stopRepeating
     * @see #flush
     */
@@ -515,31 +525,6 @@ public interface CameraDevice extends AutoCloseable {
     */
    public void waitUntilIdle() throws CameraAccessException;

    /**
     * Set the listener object to call when an asynchronous device event occurs,
     * such as errors or idle notifications.
     *
     * <p>The events reported here are device-wide; notifications about
     * individual capture requests or capture results are reported through
     * {@link CaptureListener}.</p>
     *
     * <p>If the camera device is idle when the listener is set, then the
     * {@link StateListener#onIdle} method will be immediately called,
     * even if the device has never been active before.
     * </p>
     *
     * @param listener the CameraDeviceListener to send device-level event
     * notifications to. Setting this to null will stop notifications.
     * @param handler the handler on which the listener should be invoked, or
     * {@code null} to use the current thread's {@link android.os.Looper looper}.
     *
     * @throws IllegalArgumentException if handler is null, the listener is
     * not null, and the calling thread has no looper
     *
     * @hide
     */
    public void setDeviceListener(StateListener listener, Handler handler);

    /**
     * Flush all captures currently pending and in-progress as fast as
     * possible.
@@ -577,13 +562,24 @@ public interface CameraDevice extends AutoCloseable {
    public void flush() throws CameraAccessException;

    /**
     * Close the connection to this camera device. After this call, all calls to
     * Close the connection to this camera device.
     *
     * <p>After this call, all calls to
     * the camera device interface will throw a {@link IllegalStateException},
     * except for calls to close().
     * except for calls to close(). Once the device has fully shut down, the
     * {@link StateListener#onClosed} callback will be called, and the camera is
     * free to be re-opened.</p>
     *
     * <p>After this call, besides the final {@link StateListener#onClosed} call, no calls to the
     * device's {@link StateListener} will occur, and any remaining submitted capture requests will
     * not fire their {@link CaptureListener} callbacks.</p>
     *
     * <p>To shut down as fast as possible, call the {@link #flush} method and then {@link #close}
     * once the flush completes. This will discard some capture requests, but results in faster
     * shutdown.</p>
     */
    @Override
    public void close();
    // TODO: We should decide on the behavior of in-flight requests should be on close.

    /**
     * <p>A listener for tracking the progress of a {@link CaptureRequest}
@@ -713,6 +709,9 @@ public interface CameraDevice extends AutoCloseable {
     * A listener for notifications about the state of a camera
     * device.
     *
     * <p>A listener must be provided to the {@link CameraManager#openCamera}
     * method to open a camera device.</p>
     *
     * <p>These events include notifications about the device becoming idle (
     * allowing for {@link #configureOutputs} to be called), about device
     * disconnection, and about unexpected device errors.</p>
@@ -722,7 +721,7 @@ public interface CameraDevice extends AutoCloseable {
     * the {@link #capture}, {@link #captureBurst}, {@link
     * #setRepeatingRequest}, or {@link #setRepeatingBurst} methods.
     *
     * @see #setDeviceListener
     * @see CameraManager#openCamera
     */
    public static abstract class StateListener {
       /**
+13 −16
Original line number Diff line number Diff line
@@ -197,6 +197,8 @@ public final class CameraManager {
     * {@link #openCamera}.
     *
     * @param cameraId The unique identifier of the camera device to open
     * @param listener The listener for the camera. Must not be null.
     * @param handler  The handler to call the listener on. Must not be null.
     *
     * @throws CameraAccessException if the camera is disabled by device policy,
     * or too many camera devices are already open, or the cameraId does not match
@@ -204,11 +206,14 @@ public final class CameraManager {
     *
     * @throws SecurityException if the application does not have permission to
     * access the camera
     * @throws IllegalArgumentException if listener or handler is null.
     *
     * @see #getCameraIdList
     * @see android.app.admin.DevicePolicyManager#setCameraDisabled
     */
    private CameraDevice openCamera(String cameraId) throws CameraAccessException {
    private void openCameraDeviceUserAsync(String cameraId,
            CameraDevice.StateListener listener, Handler handler)
            throws CameraAccessException {
        try {

            synchronized (mLock) {
@@ -216,7 +221,10 @@ public final class CameraManager {
                ICameraDeviceUser cameraUser;

                android.hardware.camera2.impl.CameraDevice device =
                        new android.hardware.camera2.impl.CameraDevice(cameraId);
                        new android.hardware.camera2.impl.CameraDevice(
                                cameraId,
                                listener,
                                handler);

                BinderHolder holder = new BinderHolder();
                mCameraService.connectDevice(device.getCallbacks(),
@@ -225,10 +233,9 @@ public final class CameraManager {
                cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());

                // TODO: factor out listener to be non-nested, then move setter to constructor
                // For now, calling setRemoteDevice will fire initial
                // onOpened/onUnconfigured callbacks.
                device.setRemoteDevice(cameraUser);

                return device;

            }

        } catch (NumberFormatException e) {
@@ -238,7 +245,6 @@ public final class CameraManager {
            throw e.asChecked();
        } catch (RemoteException e) {
            // impossible
            return null;
        }
    }

@@ -303,16 +309,7 @@ public final class CameraManager {
            }
        }

        final CameraDevice camera = openCamera(cameraId);
        camera.setDeviceListener(listener, handler);

        // TODO: make truly async in the camera service
        handler.post(new Runnable() {
            @Override
            public void run() {
                listener.onOpened(camera);
            }
        });
        openCameraDeviceUserAsync(cameraId, listener, handler);
    }

    /**
+148 −55
Original line number Diff line number Diff line
@@ -55,8 +55,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
    private final Object mLock = new Object();
    private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();

    private StateListener mDeviceListener;
    private Handler mDeviceHandler;
    private final StateListener mDeviceListener;
    private final Handler mDeviceHandler;

    private boolean mIdle = true;

    private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
            new SparseArray<CaptureListenerHolder>();
@@ -67,8 +69,72 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {

    private final String mCameraId;

    public CameraDevice(String cameraId) {
    // Runnables for all state transitions, except error, which needs the
    // error code argument

    private final Runnable mCallOnOpened = new Runnable() {
        public void run() {
            if (!CameraDevice.this.isClosed()) {
                mDeviceListener.onOpened(CameraDevice.this);
            }
        }
    };

    private final Runnable mCallOnUnconfigured = new Runnable() {
        public void run() {
            if (!CameraDevice.this.isClosed()) {
                mDeviceListener.onUnconfigured(CameraDevice.this);
            }
        }
    };

    private final Runnable mCallOnActive = new Runnable() {
        public void run() {
            if (!CameraDevice.this.isClosed()) {
                mDeviceListener.onActive(CameraDevice.this);
            }
        }
    };

    private final Runnable mCallOnBusy = new Runnable() {
        public void run() {
            if (!CameraDevice.this.isClosed()) {
                mDeviceListener.onBusy(CameraDevice.this);
            }
        }
    };

    private final Runnable mCallOnClosed = new Runnable() {
        public void run() {
            if (!CameraDevice.this.isClosed()) {
                mDeviceListener.onClosed(CameraDevice.this);
            }
        }
    };

    private final Runnable mCallOnIdle = new Runnable() {
        public void run() {
            if (!CameraDevice.this.isClosed()) {
                mDeviceListener.onIdle(CameraDevice.this);
            }
        }
    };

    private final Runnable mCallOnDisconnected = new Runnable() {
        public void run() {
            if (!CameraDevice.this.isClosed()) {
                mDeviceListener.onDisconnected(CameraDevice.this);
            }
        }
    };

    public CameraDevice(String cameraId, StateListener listener, Handler handler) {
        if (cameraId == null || listener == null || handler == null) {
            throw new IllegalArgumentException("Null argument given");
        }
        mCameraId = cameraId;
        mDeviceListener = listener;
        mDeviceHandler = handler;
        TAG = String.format("CameraDevice-%s-JV", mCameraId);
        DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    }
@@ -79,7 +145,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {

    public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
        // TODO: Move from decorator to direct binder-mediated exceptions
        synchronized(mLock) {
            mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);

            mDeviceHandler.post(mCallOnOpened);
            mDeviceHandler.post(mCallOnUnconfigured);
        }
    }

    @Override
@@ -89,7 +160,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {

    @Override
    public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
        // Treat a null input the same an empty list
        if (outputs == null) {
            outputs = new ArrayList<Surface>();
        }
        synchronized (mLock) {
            checkIfCameraClosed();

            HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
            List<Integer> deleteList = new ArrayList<Integer>();        // Streams to delete

@@ -105,9 +182,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
                }
            }

            mDeviceHandler.post(mCallOnBusy);
            stopRepeating();

            try {
                // TODO: mRemoteDevice.beginConfigure
                mRemoteDevice.waitUntilIdle();

                // TODO: mRemoteDevice.beginConfigure
                // Delete all streams first (to free up HW resources)
                for (Integer streamId : deleteList) {
                    mRemoteDevice.deleteStream(streamId);
@@ -126,7 +207,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
            } catch (CameraRuntimeException e) {
                if (e.getReason() == CAMERA_IN_USE) {
                    throw new IllegalStateException("The camera is currently busy." +
                            " You must call waitUntilIdle before trying to reconfigure.");
                            " You must wait until the previous operation completes.");
                }

                throw e.asChecked();
@@ -134,6 +215,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
                // impossible
                return;
            }

            if (outputs.size() > 0) {
                mDeviceHandler.post(mCallOnIdle);
            } else {
                mDeviceHandler.post(mCallOnUnconfigured);
            }
        }
    }

@@ -141,6 +228,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
    public CaptureRequest.Builder createCaptureRequest(int templateType)
            throws CameraAccessException {
        synchronized (mLock) {
            checkIfCameraClosed();

            CameraMetadataNative templatedRequest = new CameraMetadataNative();

@@ -188,7 +276,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
        }

        synchronized (mLock) {

            checkIfCameraClosed();
            int requestId;

            try {
@@ -208,6 +296,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
                mRepeatingRequestIdStack.add(requestId);
            }

            if (mIdle) {
                mDeviceHandler.post(mCallOnActive);
            }
            mIdle = false;

            return requestId;
        }
    }
@@ -233,7 +326,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
    public void stopRepeating() throws CameraAccessException {

        synchronized (mLock) {

            checkIfCameraClosed();
            while (!mRepeatingRequestIdStack.isEmpty()) {
                int requestId = mRepeatingRequestIdStack.pop();

@@ -269,21 +362,12 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
        }
    }

    @Override
    public void setDeviceListener(StateListener listener, Handler handler) {
        synchronized (mLock) {
            if (listener != null) {
                handler = checkHandler(handler);
            }

            mDeviceListener = listener;
            mDeviceHandler = handler;
        }
    }

    @Override
    public void flush() throws CameraAccessException {
        synchronized (mLock) {
            checkIfCameraClosed();

            mDeviceHandler.post(mCallOnBusy);
            try {
                mRemoteDevice.flush();
            } catch (CameraRuntimeException e) {
@@ -297,9 +381,6 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {

    @Override
    public void close() {

        // TODO: every method should throw IllegalStateException after close has been called

        synchronized (mLock) {

            try {
@@ -312,8 +393,11 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
                // impossible
            }

            mRemoteDevice = null;
            if (mRemoteDevice != null) {
                mDeviceHandler.post(mCallOnClosed);
            }

            mRemoteDevice = null;
        }
    }

@@ -399,49 +483,44 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {

        @Override
        public void onCameraError(final int errorCode) {
            synchronized (mLock) {
                if (CameraDevice.this.mDeviceListener == null) return;
                final StateListener listener = CameraDevice.this.mDeviceListener;
            Runnable r = null;
            if (isClosed()) return;

            synchronized(mLock) {
                switch (errorCode) {
                    case ERROR_CAMERA_DISCONNECTED:
                        r = new Runnable() {
                            public void run() {
                                listener.onDisconnected(CameraDevice.this);
                            }
                        };
                        r = mCallOnDisconnected;
                        break;
                    default:
                        Log.e(TAG, "Unknown error from camera device: " + errorCode);
                        // no break
                    case ERROR_CAMERA_DEVICE:
                    case ERROR_CAMERA_SERVICE:
                        r = new Runnable() {
                            public void run() {
                                listener.onError(CameraDevice.this, errorCode);
                                if (!CameraDevice.this.isClosed()) {
                                    mDeviceListener.onError(CameraDevice.this, errorCode);
                                }
                            }
                        };
                        break;
                    default:
                        Log.e(TAG, "Unknown error from camera device: " + errorCode);
                }
                if (r != null) {
                CameraDevice.this.mDeviceHandler.post(r);
            }
        }
        }

        @Override
        public void onCameraIdle() {
            if (isClosed()) return;

            if (DEBUG) {
                Log.d(TAG, "Camera now idle");
            }
            synchronized (mLock) {
                if (CameraDevice.this.mDeviceListener == null) return;
                final StateListener listener = CameraDevice.this.mDeviceListener;
                Runnable r = new Runnable() {
                    public void run() {
                        listener.onIdle(CameraDevice.this);
                if (!CameraDevice.this.mIdle) {
                    CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
                }
                };
                CameraDevice.this.mDeviceHandler.post(r);
                CameraDevice.this.mIdle = true;
            }
        }

@@ -461,15 +540,19 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
                return;
            }

            if (isClosed()) return;

            // Dispatch capture start notice
            holder.getHandler().post(
                new Runnable() {
                    public void run() {
                        if (!CameraDevice.this.isClosed()) {
                            holder.getListener().onCaptureStarted(
                                CameraDevice.this,
                                holder.getRequest(),
                                timestamp);
                        }
                    }
                });
        }

@@ -503,6 +586,8 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
                return;
            }

            if (isClosed()) return;

            final CaptureRequest request = holder.getRequest();
            final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);

@@ -510,11 +595,13 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
                new Runnable() {
                    @Override
                    public void run() {
                        if (!CameraDevice.this.isClosed()){
                            holder.getListener().onCaptureCompleted(
                                CameraDevice.this,
                                request,
                                resultAsCapture);
                        }
                    }
                });
        }

@@ -541,4 +628,10 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice {
            throw new IllegalStateException("CameraDevice was already closed");
        }
    }

    private boolean isClosed() {
        synchronized(mLock) {
            return (mRemoteDevice == null);
        }
    }
}