107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin/*
207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * Copyright 2013 The Android Open Source Project
307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin *
407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * Licensed under the Apache License, Version 2.0 (the "License");
507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * you may not use this file except in compliance with the License.
607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * You may obtain a copy of the License at
707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin *
807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin *      http://www.apache.org/licenses/LICENSE-2.0
907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin *
1007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * Unless required by applicable law or agreed to in writing, software
1107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * distributed under the License is distributed on an "AS IS" BASIS,
1207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * See the License for the specific language governing permissions and
1407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * limitations under the License.
1507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin */
1607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinpackage com.android.ex.camera2.blocking;
1707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
1807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport android.hardware.camera2.CameraAccessException;
1907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport android.hardware.camera2.CameraDevice;
2007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport android.hardware.camera2.CameraManager;
2107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport android.os.ConditionVariable;
2207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport android.os.Handler;
2307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport android.os.Looper;
2407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport android.util.Log;
2507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
2607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport com.android.ex.camera2.exceptions.TimeoutRuntimeException;
2707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
2807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinimport java.util.Objects;
2907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
3007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin/**
3107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * Expose {@link CameraManager} functionality with blocking functions.
3207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin *
3307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * <p>Safe to use at the same time as the regular CameraManager, so this does not
3407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * duplicate any functionality that is already blocking.</p>
3507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin *
3607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin * <p>Be careful when using this from UI thread! This function will typically block
375a772174d14175474e76701b07cc0be86c3df32aJiawen Chen * for about 500ms when successful, and as long as {@value #OPEN_TIME_OUT_MS}ms when timing out.</p>
3807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin */
3907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkinpublic class BlockingCameraManager {
4007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
415a772174d14175474e76701b07cc0be86c3df32aJiawen Chen    private static final String TAG = "BlockingCameraManager";
4207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
4307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
445a772174d14175474e76701b07cc0be86c3df32aJiawen Chen    private static final int OPEN_TIME_OUT_MS = 2000; // ms time out for openCamera
4507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
4607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    /**
4707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * Exception thrown by {@link #openCamera} if the open fails asynchronously.
4807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     */
4907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    public static class BlockingOpenException extends Exception {
5007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        /**
515a772174d14175474e76701b07cc0be86c3df32aJiawen Chen         * Suppress Eclipse warning
5207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         */
5307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private static final long serialVersionUID = 12397123891238912L;
5407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
5507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public static final int ERROR_DISCONNECTED = 0; // Does not clash with ERROR_...
5607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
5707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private final int mError;
5807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
5907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public boolean wasDisconnected() {
6007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            return mError == ERROR_DISCONNECTED;
6107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
6207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
6307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public boolean wasError() {
6407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            return mError != ERROR_DISCONNECTED;
6507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
6607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
6707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        /**
6807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         * Returns the error code {@link ERROR_DISCONNECTED} if disconnected, or one of
69bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala         * {@code CameraDevice.StateCallback#ERROR_*} if there was another error.
7007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         *
7107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         * @return int Disconnect/error code
7207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         */
7307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public int getCode() {
7407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            return mError;
7507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
7607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
7707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        /**
7807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         * Thrown when camera device enters error state during open, or if
7907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         * it disconnects.
8007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         *
8107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         * @param errorCode
8207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         * @param message
8307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         *
84bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala         * @see {@link CameraDevice.StateCallback#ERROR_CAMERA_DEVICE}
8507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin         */
8607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public BlockingOpenException(int errorCode, String message) {
8707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            super(message);
8807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            mError = errorCode;
8907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
9007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    }
9107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
9207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    private final CameraManager mManager;
9307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
9407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    /**
9507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * Create a new blocking camera manager.
9607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
9707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @param manager
9807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            CameraManager returned by
9907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            {@code Context.getSystemService(Context.CAMERA_SERVICE)}
10007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     */
10107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    public BlockingCameraManager(CameraManager manager) {
10207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        if (manager == null) {
10307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            throw new IllegalArgumentException("manager must not be null");
10407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
10507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        mManager = manager;
10607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    }
10707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
10807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    /**
10907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * Open the camera, blocking it until it succeeds or fails.
11007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
11107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * <p>Note that the Handler provided must not be null. Furthermore, if there is a handler,
11207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * its Looper must not be the current thread's Looper. Otherwise we'd never receive
11307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * the callbacks from the CameraDevice since this function would prevent them from being
11407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * processed.</p>
11507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
11607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * <p>Throws {@link CameraAccessException} for the same reason {@link CameraManager#openCamera}
11707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * does.</p>
11807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
11907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * <p>Throws {@link BlockingOpenException} when the open fails asynchronously (due to
120bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala     * {@link CameraDevice.StateCallback#onDisconnected(CameraDevice)} or
121bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala     * ({@link CameraDevice.StateCallback#onError(CameraDevice)}.</p>
12207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
12307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * <p>Throws {@link TimeoutRuntimeException} if opening times out. This is usually
12407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * highly unrecoverable, and all future calls to opening that camera will fail since the
12507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * service will think it's busy. This class will do its best to clean up eventually.</p>
12607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
12707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @param cameraId
12807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            Id of the camera
12907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @param listener
13007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            Listener to the camera. onOpened, onDisconnected, onError need not be implemented.
13107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @param handler
13207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            Handler which to run the listener on. Must not be null.
13307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
13407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @return CameraDevice
13507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
13607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @throws IllegalArgumentException
13707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            If the handler is null, or if the handler's looper is current.
13807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @throws CameraAccessException
13907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            If open fails immediately.
14007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @throws BlockingOpenException
14107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            If open fails after blocking for some amount of time.
14207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * @throws TimeoutRuntimeException
14307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *            If opening times out. Typically unrecoverable.
14407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     */
145bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala    public CameraDevice openCamera(String cameraId, CameraDevice.StateCallback listener,
14607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            Handler handler) throws CameraAccessException, BlockingOpenException {
14707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
14807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        if (handler == null) {
14907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            throw new IllegalArgumentException("handler must not be null");
15007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        } else if (handler.getLooper() == Looper.myLooper()) {
15107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            throw new IllegalArgumentException("handler's looper must not be the current looper");
15207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
15307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
15407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        return (new OpenListener(mManager, cameraId, listener, handler)).blockUntilOpen();
15507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    }
15607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
15707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    private static void assertEquals(Object a, Object b) {
15807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        if (!Objects.equals(a, b)) {
15907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            throw new AssertionError("Expected " + a + ", but got " + b);
16007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
16107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    }
16207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
16307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    /**
16407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * Block until CameraManager#openCamera finishes with onOpened/onError/onDisconnected
16507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
166bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala     * <p>Pass-through all StateCallback changes to the proxy.</p>
16707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     *
1685a772174d14175474e76701b07cc0be86c3df32aJiawen Chen     * <p>Time out after {@link #OPEN_TIME_OUT_MS} and unblock. Clean up camera if it arrives
16907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     * later.</p>
17007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin     */
171bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala    private class OpenListener extends CameraDevice.StateCallback {
17207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private static final int ERROR_UNINITIALIZED = -1;
17307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
17407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private final String mCameraId;
17507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
176bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala        private final CameraDevice.StateCallback mProxy;
17707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
17807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private final Object mLock = new Object();
17907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private final ConditionVariable mDeviceReady = new ConditionVariable();
18007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
18107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private CameraDevice mDevice = null;
18207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private boolean mSuccess = false;
18307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private int mError = ERROR_UNINITIALIZED;
18407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private boolean mDisconnected = false;
18507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
18607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private boolean mNoReply = true; // Start with no reply until proven otherwise
18707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private boolean mTimedOut = false;
18807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
18907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        OpenListener(CameraManager manager, String cameraId,
190bb013aa3e197e881756be5ad13e6ad30bfb4aeffEino-Ville Talvala                CameraDevice.StateCallback listener, Handler handler)
19107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                throws CameraAccessException {
19207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            mCameraId = cameraId;
19307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            mProxy = listener;
19407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            manager.openCamera(cameraId, this, handler);
19507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
19607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
19707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        // Freebie check to make sure we aren't calling functions multiple times.
19807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        // We should still test the state interactions in a separate more thorough test.
19907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        private void assertInitialState() {
20007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            assertEquals(null, mDevice);
20107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            assertEquals(false, mDisconnected);
20207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            assertEquals(ERROR_UNINITIALIZED, mError);
20307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            assertEquals(false, mSuccess);
20407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
20507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
20607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        @Override
20707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public void onOpened(CameraDevice camera) {
20807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            if (VERBOSE) {
20907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                Log.v(TAG, "onOpened: camera " + ((camera != null) ? camera.getId() : "null"));
21007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
21107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
21207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            synchronized (mLock) {
21307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                assertInitialState();
21407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mNoReply = false;
21507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mSuccess = true;
21607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mDevice = camera;
21707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mDeviceReady.open();
21807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
21907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                if (mTimedOut && camera != null) {
22007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    camera.close();
22107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    return;
22207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                }
22307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
22407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
22507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            if (mProxy != null) mProxy.onOpened(camera);
22607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
22707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
22807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        @Override
22907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public void onDisconnected(CameraDevice camera) {
23007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            if (VERBOSE) {
23107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                Log.v(TAG, "onDisconnected: camera "
23207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                        + ((camera != null) ? camera.getId() : "null"));
23307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
23407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
23507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            synchronized (mLock) {
23607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                assertInitialState();
23707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mNoReply = false;
23807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mDisconnected = true;
23907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mDevice = camera;
24007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mDeviceReady.open();
24107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
24207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                if (mTimedOut && camera != null) {
24307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    camera.close();
24407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    return;
24507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                }
24607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
24707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
24807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            if (mProxy != null) mProxy.onDisconnected(camera);
24907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
25007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
25107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        @Override
25207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public void onError(CameraDevice camera, int error) {
25307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            if (VERBOSE) {
25407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                Log.v(TAG, "onError: camera " + ((camera != null) ? camera.getId() : "null"));
25507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
25607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
25707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            if (error <= 0) {
25807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                throw new AssertionError("Expected error to be a positive number");
25907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
26007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
26107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            synchronized (mLock) {
26207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                // Don't assert initial state. Error can happen later.
26307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mNoReply = false;
26407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mError = error;
26507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mDevice = camera;
26607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                mDeviceReady.open();
26707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
26807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                if (mTimedOut && camera != null) {
26907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    camera.close();
27007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    return;
27107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                }
27207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
27307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
27407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            if (mProxy != null) mProxy.onError(camera, error);
27507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
27607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
27707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        @Override
27807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        public void onClosed(CameraDevice camera) {
27907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            if (mProxy != null) mProxy.onClosed(camera);
28007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
28107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
28207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        CameraDevice blockUntilOpen() throws BlockingOpenException {
28307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            /**
28407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin             * Block until onOpened, onError, or onDisconnected
28507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin             */
2865a772174d14175474e76701b07cc0be86c3df32aJiawen Chen            if (!mDeviceReady.block(OPEN_TIME_OUT_MS)) {
28707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
28807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                synchronized (mLock) {
28907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    if (mNoReply) { // Give the async camera a fighting chance (required)
29007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                        mTimedOut = true; // Clean up camera if it ever arrives later
29107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                        throw new TimeoutRuntimeException(String.format(
29207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                                "Timed out after %d ms while trying to open camera device %s",
2935a772174d14175474e76701b07cc0be86c3df32aJiawen Chen                                OPEN_TIME_OUT_MS, mCameraId));
29407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    }
29507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                }
29607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
29707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
29807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            synchronized (mLock) {
29907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                /**
30007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                 * Determine which state we ended up in:
30107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                 *
30207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                 * - Throw exceptions for onError/onDisconnected
30307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                 * - Return device for onOpened
30407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                 */
30507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                if (!mSuccess && mDevice != null) {
30607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    mDevice.close();
30707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                }
30807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin
30907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                if (mSuccess) {
31007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    return mDevice;
31107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                } else {
31207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    if (mDisconnected) {
31307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                        throw new BlockingOpenException(
31407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                                BlockingOpenException.ERROR_DISCONNECTED,
31507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                                "Failed to open camera device: it is disconnected");
31607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    } else if (mError != ERROR_UNINITIALIZED) {
31707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                        throw new BlockingOpenException(
31807f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                                mError,
31907f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                                "Failed to open camera device: error code " + mError);
32007f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    } else {
32107f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                        throw new AssertionError("Failed to open camera device (impl bug)");
32207f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                    }
32307f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin                }
32407f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin            }
32507f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin        }
32607f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin    }
32707f09b47112dc1094649da00f7e86024b67d5777Igor Murashkin}
328