1b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde/*
2b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * Copyright (C) 2015 The Android Open Source Project
3b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde *
4b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * Licensed under the Apache License, Version 2.0 (the "License");
5b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * you may not use this file except in compliance with the License.
6b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * You may obtain a copy of the License at
7b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde *
8b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde *      http://www.apache.org/licenses/LICENSE-2.0
9b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde *
10b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * Unless required by applicable law or agreed to in writing, software
11b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * distributed under the License is distributed on an "AS IS" BASIS,
12b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * See the License for the specific language governing permissions and
14b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * limitations under the License.
15b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde */
16b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
17b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdepackage com.android.camera.device;
18b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
19b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport android.content.Context;
20b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport android.os.Handler;
21b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
22b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.camera.async.HandlerFactory;
23b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.camera.async.Lifetime;
24b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.camera.async.SafeCloseable;
25b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.camera.debug.Log.Tag;
26b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.camera.debug.Logger;
27b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.ex.camera2.portability.CameraAgent;
28b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.ex.camera2.portability.CameraAgent.CameraOpenCallback;
29b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.ex.camera2.portability.CameraAgent.CameraProxy;
30b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.ex.camera2.portability.CameraAgentFactory;
31b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport com.android.ex.camera2.portability.CameraAgentFactory.CameraApi;
32b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
33b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport java.util.concurrent.ExecutorService;
34b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdeimport java.util.concurrent.atomic.AtomicBoolean;
35b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
36d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohdeimport javax.annotation.ParametersAreNonnullByDefault;
37d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde
38b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde/**
39b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * Set of device actions for opening and closing a single portability
40b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde * layer camera device.
41b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde */
42d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde@ParametersAreNonnullByDefault
43b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohdepublic class PortabilityCameraActions implements SingleDeviceActions<CameraProxy> {
44d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde    private static final Tag TAG = new Tag("PortCamAct");
45b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
461d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde    private final CameraDeviceKey mId;
47b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private final HandlerFactory mHandlerFactory;
48b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private final ExecutorService mBackgroundRunner;
49b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private final Context mContext;
50b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private final CameraApi mApiVersion;
51b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private final Logger mLogger;
52b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
53b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    public PortabilityCameraActions(
541d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde          CameraDeviceKey id,
55b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde          Context context,
56b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde          CameraApi apiVersion,
57b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde          ExecutorService backgroundRunner,
58b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde          HandlerFactory handlerFactory,
59b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde          Logger.Factory logFactory) {
60b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        mId = id;
61b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        mContext = context;
62b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        mApiVersion = apiVersion;
63b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        mBackgroundRunner = backgroundRunner;
64b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        mHandlerFactory = handlerFactory;
65b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        mLogger = logFactory.create(TAG);
66b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
67b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        mLogger.d("Created Camera2Request");
68b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    }
69b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
70b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    @Override
71b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    public void executeOpen(SingleDeviceOpenListener<CameraProxy> openListener,
721d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde          Lifetime deviceLifetime) throws UnsupportedOperationException {
73d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde        mLogger.i("executeOpen(id: " + mId.getCameraId() + ")");
74e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin        try {
75e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin            CameraAgent agent = CameraAgentFactory.getAndroidCameraAgent(mContext, mApiVersion);
76e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin            deviceLifetime.add(new CameraAgentRecycler(mApiVersion, mLogger));
77b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
78e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin            mBackgroundRunner.execute(new OpenCameraRunnable(agent, mId.getCameraId().getLegacyValue(),
79e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin                    mHandlerFactory.create(deviceLifetime, "Camera2 Lifetime"),
80e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin                    openListener, mLogger));
81e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin        } catch (AssertionError e) {
82e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin            openListener.onDeviceOpenException(e);
83e3ba951d640316c598f0e690422879e8d4ba0a62Pengchong Jin        }
84b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    }
85b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
86b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    @Override
87b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    public void executeClose(SingleDeviceCloseListener closeListener, CameraProxy device) {
88d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde        mLogger.i("executeClose(" + device.getCameraId() + ")");
89b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        mBackgroundRunner.execute(new CloseCameraRunnable(device, device.getAgent(),
90b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde              closeListener, mLogger));
91b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    }
92b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
93b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    /**
94b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     * Recycles camera agents and ensures that recycle is only called
95b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     * once per instance.
96b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     */
97b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private static class CameraAgentRecycler implements SafeCloseable {
98b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final CameraApi mCameraApi;
99b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final Logger mLogger;
100b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final AtomicBoolean mIsClosed;
101b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
102b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public CameraAgentRecycler(CameraApi cameraApi, Logger logger) {
103b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mCameraApi = cameraApi;
104b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mLogger = logger;
105b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mIsClosed = new AtomicBoolean(false);
106b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
107b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
108b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        @Override
109b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public void close() {
110b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            if (!mIsClosed.getAndSet(true)) {
111b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mLogger.d("Recycling CameraAgentFactory for CameraApi: " + mCameraApi);
112b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                CameraAgentFactory.recycle(mCameraApi);
113b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            }
114b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
115b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    }
116b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
117b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    /**
118b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     * Internal runnable that executes a CameraManager openCamera call.
119b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     */
120b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private static class OpenCameraRunnable implements Runnable {
121b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final SingleDeviceOpenListener<CameraProxy> mOpenListener;
122b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final int mCameraId;
123b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final Handler mHandler;
124b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final CameraAgent mCameraAgent;
125b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final Logger mLogger;
126b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
127b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public OpenCameraRunnable(CameraAgent cameraAgent, int cameraId,
128b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde              Handler handler, SingleDeviceOpenListener<CameraProxy> openListener,
129b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde              Logger logger) {
130b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mCameraAgent = cameraAgent;
131b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mCameraId = cameraId;
132b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mHandler = handler;
133b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mOpenListener = openListener;
134b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mLogger = logger;
135b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
136b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
137b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        @Override
138b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public void run() {
139b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            try {
140d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mLogger.i("mCameraAgent.openCamera(id: " + mCameraId + ")");
141b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mCameraAgent.openCamera(mHandler, mCameraId,
142b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                      new OpenCameraStateCallback(mOpenListener, mLogger));
143b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            } catch (SecurityException e) {
144b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mOpenListener.onDeviceOpenException(e);
145b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            }
146b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
147b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    }
148b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
149b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    /**
150b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     * Internal runnable that executes a close on a cameraDevice.
151b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     */
152b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private static class CloseCameraRunnable implements Runnable {
153b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final SingleDeviceCloseListener mCloseListener;
154b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final CameraProxy mCameraDevice;
155b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final CameraAgent mCameraAgent;
156b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final Logger mLogger;
157b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
158b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public CloseCameraRunnable(CameraProxy cameraDevice, CameraAgent cameraAgent,
159b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde              SingleDeviceCloseListener closeListener, Logger logger) {
160b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mCameraDevice = cameraDevice;
161b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mCameraAgent = cameraAgent;
162b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mCloseListener = closeListener;
163b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mLogger = logger;
164b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
165b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
166b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        @Override
167b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public void run() {
168b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            try {
169d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mLogger.i("mCameraAgent.closeCamera(id: " + mCameraDevice.getCameraId() + ")");
170b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mCameraAgent.closeCamera(mCameraDevice, true /* synchronous */);
171b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mCloseListener.onDeviceClosed();
172b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            } catch (Exception e) {
173b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mCloseListener.onDeviceClosingException(e);
174b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            }
175b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
176b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    }
177b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
178b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    /**
179b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     * Internal callback that provides a camera device to a future.
180b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde     */
181b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    private static class OpenCameraStateCallback implements CameraOpenCallback {
182b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final SingleDeviceOpenListener<CameraProxy> mOpenListener;
183b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private final Logger mLogger;
184b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        private boolean mHasBeenCalled = false;
185b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
186b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public OpenCameraStateCallback(SingleDeviceOpenListener<CameraProxy> openListener,
187b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde              Logger logger) {
188b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mOpenListener = openListener;
189b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            mLogger = logger;
190b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
191b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
192b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        @Override
193b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public void onCameraOpened(CameraProxy camera) {
194b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            if (!called()) {
195d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mLogger.i("onCameraOpened(id: " + camera.getCameraId() + ")");
196b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mOpenListener.onDeviceOpened(camera);
197b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            }
198b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
199b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
200b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        @Override
201b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public void onCameraDisabled(int cameraId) {
202b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            if (!called()) {
203d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mLogger.w("onCameraDisabled(id: " + cameraId + ")");
204b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
205b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            }
206b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
207b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
208b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        @Override
209b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public void onDeviceOpenFailure(int cameraId, String info) {
210b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            if (!called()) {
211d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mLogger.e("onDeviceOpenFailure(id: " + cameraId
212b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                      + ", info: " + info + ")");
213b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
214b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            }
215b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
216b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
217b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        @Override
218b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public void onDeviceOpenedAlready(int cameraId, String info) {
219b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            if (!called()) {
220d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mLogger.w("onDeviceOpenedAlready(id: " + cameraId
221b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                      + ", info: " + info + ")");
222b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
223b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            }
224b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
225b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde
226b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        @Override
227b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        public void onReconnectionFailure(CameraAgent mgr, String info) {
228b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            if (!called()) {
229d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mLogger.w("onReconnectionFailure(info: " + info + ")");
230b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde                mOpenListener.onDeviceOpenException(new CameraOpenException(-1));
231b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde            }
232b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde        }
233d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde
234d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde        private boolean called() {
235d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde            boolean result = mHasBeenCalled;
236d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde            if (!mHasBeenCalled) {
237d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mHasBeenCalled = true;
238d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde            } else {
239d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde                mLogger.v("Callback was re-executed.");
240d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde            }
241d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde
242d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde            return result;
243d552f16540c59b28073e526e99fbcfe7dfa5ef09Paul Rohde        }
244b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde    }
245b6a4d96a310a1dee22ddea89b09130bcda206bb3Paul Rohde}
246