blob: 920212385c2c834dae192bce29bb3803fbb8d0f2 [file] [log] [blame]
Igor Murashkin70725502013-06-25 20:27:06 +00001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070017package android.hardware.camera2.impl;
Igor Murashkin70725502013-06-25 20:27:06 +000018
Igor Murashkin57ea59b2013-08-23 16:55:57 -070019import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
20
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070021import android.hardware.camera2.CameraAccessException;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070022import android.hardware.camera2.CaptureRequest;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070023import android.hardware.camera2.CaptureResult;
Jianing Weid2c3a822014-03-27 18:27:43 -070024import android.hardware.camera2.CaptureResultExtras;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070025import android.hardware.camera2.ICameraDeviceCallbacks;
26import android.hardware.camera2.ICameraDeviceUser;
Jianing Weid2c3a822014-03-27 18:27:43 -070027import android.hardware.camera2.LongParcelable;
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -070028import android.hardware.camera2.utils.CameraBinderDecorator;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070029import android.hardware.camera2.utils.CameraRuntimeException;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070030import android.os.Handler;
Ruben Brunkdecfe952013-10-29 11:00:32 -070031import android.os.IBinder;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070032import android.os.Looper;
Ruben Brunkdecfe952013-10-29 11:00:32 -070033import android.os.RemoteException;
Igor Murashkin70725502013-06-25 20:27:06 +000034import android.util.Log;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070035import android.util.SparseArray;
Igor Murashkin70725502013-06-25 20:27:06 +000036import android.view.Surface;
37
Jianing Weid2c3a822014-03-27 18:27:43 -070038import java.util.AbstractMap.SimpleEntry;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070039import java.util.ArrayList;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070040import java.util.HashSet;
Ruben Brunkdecfe952013-10-29 11:00:32 -070041import java.util.Iterator;
Igor Murashkin70725502013-06-25 20:27:06 +000042import java.util.List;
Jianing Weid2c3a822014-03-27 18:27:43 -070043import java.util.TreeSet;
Igor Murashkin70725502013-06-25 20:27:06 +000044
45/**
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070046 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
Igor Murashkin70725502013-06-25 20:27:06 +000047 */
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070048public class CameraDevice implements android.hardware.camera2.CameraDevice {
Igor Murashkin70725502013-06-25 20:27:06 +000049
50 private final String TAG;
Zhijun Heecb323e2013-07-31 09:40:27 -070051 private final boolean DEBUG;
Igor Murashkin70725502013-06-25 20:27:06 +000052
Ruben Brunkdecfe952013-10-29 11:00:32 -070053 private static final int REQUEST_ID_NONE = -1;
54
Igor Murashkin70725502013-06-25 20:27:06 +000055 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
56 private ICameraDeviceUser mRemoteDevice;
57
58 private final Object mLock = new Object();
59 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
60
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070061 private final StateListener mDeviceListener;
62 private final Handler mDeviceHandler;
63
64 private boolean mIdle = true;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070065
66 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
67 new SparseArray<CaptureListenerHolder>();
Igor Murashkin70725502013-06-25 20:27:06 +000068
Ruben Brunkdecfe952013-10-29 11:00:32 -070069 private int mRepeatingRequestId = REQUEST_ID_NONE;
70 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
Igor Murashkin57ea59b2013-08-23 16:55:57 -070071 // Map stream IDs to Surfaces
72 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
Igor Murashkin70725502013-06-25 20:27:06 +000073
74 private final String mCameraId;
75
Jianing Weid2c3a822014-03-27 18:27:43 -070076 /**
77 * A list tracking request and its expected last frame.
78 * Updated when calling ICameraDeviceUser methods.
79 */
80 private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
81 mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
82
83 /**
84 * An object tracking received frame numbers.
85 * Updated when receiving callbacks from ICameraDeviceCallbacks.
86 */
87 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
88
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070089 // Runnables for all state transitions, except error, which needs the
90 // error code argument
91
92 private final Runnable mCallOnOpened = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -070093 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070094 public void run() {
95 if (!CameraDevice.this.isClosed()) {
96 mDeviceListener.onOpened(CameraDevice.this);
97 }
98 }
99 };
100
101 private final Runnable mCallOnUnconfigured = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700102 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700103 public void run() {
104 if (!CameraDevice.this.isClosed()) {
105 mDeviceListener.onUnconfigured(CameraDevice.this);
106 }
107 }
108 };
109
110 private final Runnable mCallOnActive = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700111 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700112 public void run() {
113 if (!CameraDevice.this.isClosed()) {
114 mDeviceListener.onActive(CameraDevice.this);
115 }
116 }
117 };
118
119 private final Runnable mCallOnBusy = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700120 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700121 public void run() {
122 if (!CameraDevice.this.isClosed()) {
123 mDeviceListener.onBusy(CameraDevice.this);
124 }
125 }
126 };
127
128 private final Runnable mCallOnClosed = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700129 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700130 public void run() {
Zhijun He2db56022014-01-02 15:12:00 -0800131 mDeviceListener.onClosed(CameraDevice.this);
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700132 }
133 };
134
135 private final Runnable mCallOnIdle = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700136 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700137 public void run() {
138 if (!CameraDevice.this.isClosed()) {
139 mDeviceListener.onIdle(CameraDevice.this);
140 }
141 }
142 };
143
144 private final Runnable mCallOnDisconnected = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700145 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700146 public void run() {
147 if (!CameraDevice.this.isClosed()) {
148 mDeviceListener.onDisconnected(CameraDevice.this);
149 }
150 }
151 };
152
153 public CameraDevice(String cameraId, StateListener listener, Handler handler) {
154 if (cameraId == null || listener == null || handler == null) {
155 throw new IllegalArgumentException("Null argument given");
156 }
Igor Murashkin70725502013-06-25 20:27:06 +0000157 mCameraId = cameraId;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700158 mDeviceListener = listener;
159 mDeviceHandler = handler;
Igor Murashkin70725502013-06-25 20:27:06 +0000160 TAG = String.format("CameraDevice-%s-JV", mCameraId);
Zhijun Heecb323e2013-07-31 09:40:27 -0700161 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +0000162 }
163
164 public CameraDeviceCallbacks getCallbacks() {
165 return mCallbacks;
166 }
167
Igor Murashkin70725502013-06-25 20:27:06 +0000168 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -0700169 // TODO: Move from decorator to direct binder-mediated exceptions
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700170 synchronized(mLock) {
171 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
172
173 mDeviceHandler.post(mCallOnOpened);
174 mDeviceHandler.post(mCallOnUnconfigured);
175 }
Igor Murashkin70725502013-06-25 20:27:06 +0000176 }
177
178 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700179 public String getId() {
180 return mCameraId;
181 }
182
183 @Override
Igor Murashkin70725502013-06-25 20:27:06 +0000184 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700185 // Treat a null input the same an empty list
186 if (outputs == null) {
187 outputs = new ArrayList<Surface>();
188 }
Igor Murashkin70725502013-06-25 20:27:06 +0000189 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700190 checkIfCameraClosed();
191
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700192 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
193 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
194
195 // Determine which streams need to be created, which to be deleted
196 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
197 int streamId = mConfiguredOutputs.keyAt(i);
198 Surface s = mConfiguredOutputs.valueAt(i);
199
200 if (!outputs.contains(s)) {
201 deleteList.add(streamId);
202 } else {
203 addSet.remove(s); // Don't create a stream previously created
204 }
205 }
206
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700207 mDeviceHandler.post(mCallOnBusy);
208 stopRepeating();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700209
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700210 try {
Ruben Brunkdecfe952013-10-29 11:00:32 -0700211 waitUntilIdle();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700212
213 // TODO: mRemoteDevice.beginConfigure
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700214 // Delete all streams first (to free up HW resources)
215 for (Integer streamId : deleteList) {
216 mRemoteDevice.deleteStream(streamId);
217 mConfiguredOutputs.delete(streamId);
218 }
219
220 // Add all new streams
221 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000222 // TODO: remove width,height,format since we are ignoring
223 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700224 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
225 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000226 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700227
228 // TODO: mRemoteDevice.endConfigure
229 } catch (CameraRuntimeException e) {
230 if (e.getReason() == CAMERA_IN_USE) {
231 throw new IllegalStateException("The camera is currently busy." +
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700232 " You must wait until the previous operation completes.");
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700233 }
234
235 throw e.asChecked();
236 } catch (RemoteException e) {
237 // impossible
238 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000239 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700240
241 if (outputs.size() > 0) {
242 mDeviceHandler.post(mCallOnIdle);
243 } else {
244 mDeviceHandler.post(mCallOnUnconfigured);
245 }
Igor Murashkin70725502013-06-25 20:27:06 +0000246 }
247 }
248
249 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700250 public CaptureRequest.Builder createCaptureRequest(int templateType)
251 throws CameraAccessException {
Igor Murashkin70725502013-06-25 20:27:06 +0000252 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700253 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000254
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700255 CameraMetadataNative templatedRequest = new CameraMetadataNative();
Igor Murashkin70725502013-06-25 20:27:06 +0000256
257 try {
258 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
259 } catch (CameraRuntimeException e) {
260 throw e.asChecked();
261 } catch (RemoteException e) {
262 // impossible
263 return null;
264 }
265
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700266 CaptureRequest.Builder builder =
267 new CaptureRequest.Builder(templatedRequest);
Igor Murashkin70725502013-06-25 20:27:06 +0000268
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700269 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000270 }
271 }
272
273 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700274 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000275 throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700276 if (DEBUG) {
277 Log.d(TAG, "calling capture");
278 }
279 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
280 requestList.add(request);
281 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000282 }
283
284 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700285 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700286 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700287 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700288 if (requests.isEmpty()) {
289 Log.w(TAG, "Capture burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700290 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700291 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700292 return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000293 }
294
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700295 /**
296 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
297 * starting and stopping repeating request and flushing.
298 *
299 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
300 * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
301 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
302 * is added to the list mFrameNumberRequestPairs.</p>
303 *
304 * @param requestId the request ID of the current repeating request.
305 *
306 * @param lastFrameNumber last frame number returned from binder.
307 */
308 private void checkEarlyTriggerSequenceComplete(
309 final int requestId, final long lastFrameNumber) {
310 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
311 // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
312 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
313 final CaptureListenerHolder holder;
314 int index = mCaptureListenerMap.indexOfKey(requestId);
315 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
316 if (holder != null) {
317 mCaptureListenerMap.removeAt(index);
Jianing Weibaf0c652014-04-18 17:35:00 -0700318 if (DEBUG) {
319 Log.v(TAG, String.format(
320 "remove holder for requestId %d, "
321 + "because lastFrame is %d.",
322 requestId, lastFrameNumber));
323 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700324 }
325
326 if (holder != null) {
327 if (DEBUG) {
328 Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
329 + " request did not reach HAL");
330 }
331
332 Runnable resultDispatch = new Runnable() {
333 @Override
334 public void run() {
335 if (!CameraDevice.this.isClosed()) {
336 if (DEBUG) {
337 Log.d(TAG, String.format(
338 "early trigger sequence complete for request %d",
339 requestId));
340 }
341 if (lastFrameNumber < Integer.MIN_VALUE
342 || lastFrameNumber > Integer.MAX_VALUE) {
343 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
344 }
345 holder.getListener().onCaptureSequenceCompleted(
346 CameraDevice.this,
347 requestId,
348 (int)lastFrameNumber);
349 }
350 }
351 };
352 holder.getHandler().post(resultDispatch);
353 } else {
354 Log.w(TAG, String.format(
355 "did not register listener to request %d",
356 requestId));
357 }
358 } else {
359 mFrameNumberRequestPairs.add(
360 new SimpleEntry<Long, Integer>(lastFrameNumber,
361 requestId));
362 }
363 }
364
Jianing Weid2c3a822014-03-27 18:27:43 -0700365 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700366 Handler handler, boolean repeating) throws CameraAccessException {
367
368 // Need a valid handler, or current thread needs to have a looper, if
369 // listener is valid
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700370 if (listener != null) {
371 handler = checkHandler(handler);
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700372 }
Igor Murashkin70725502013-06-25 20:27:06 +0000373
374 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700375 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000376 int requestId;
377
Ruben Brunke73b41b2013-11-07 19:30:43 -0800378 if (repeating) {
379 stopRepeating();
380 }
381
Jianing Weid2c3a822014-03-27 18:27:43 -0700382 LongParcelable lastFrameNumberRef = new LongParcelable();
Igor Murashkin70725502013-06-25 20:27:06 +0000383 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700384 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
385 /*out*/lastFrameNumberRef);
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700386 if (DEBUG) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700387 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
388 }
Igor Murashkin70725502013-06-25 20:27:06 +0000389 } catch (CameraRuntimeException e) {
390 throw e.asChecked();
391 } catch (RemoteException e) {
392 // impossible
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700393 return -1;
Igor Murashkin70725502013-06-25 20:27:06 +0000394 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700395
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700396 if (listener != null) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700397 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
398 requestList, handler, repeating));
Jianing Weibaf0c652014-04-18 17:35:00 -0700399 } else {
400 if (DEBUG) {
401 Log.d(TAG, "Listen for request " + requestId + " is null");
402 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700403 }
Igor Murashkin70725502013-06-25 20:27:06 +0000404
Jianing Weid2c3a822014-03-27 18:27:43 -0700405 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700406
Igor Murashkin70725502013-06-25 20:27:06 +0000407 if (repeating) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700408 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700409 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700410 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700411 mRepeatingRequestId = requestId;
Jianing Weid2c3a822014-03-27 18:27:43 -0700412 } else {
413 mFrameNumberRequestPairs.add(
414 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
Igor Murashkin70725502013-06-25 20:27:06 +0000415 }
416
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700417 if (mIdle) {
418 mDeviceHandler.post(mCallOnActive);
419 }
420 mIdle = false;
421
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700422 return requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000423 }
424 }
425
426 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700427 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700428 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700429 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
430 requestList.add(request);
431 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000432 }
433
434 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700435 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700436 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700437 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700438 if (requests.isEmpty()) {
439 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700440 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700441 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700442 return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000443 }
444
445 @Override
446 public void stopRepeating() throws CameraAccessException {
447
448 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700449 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700450 if (mRepeatingRequestId != REQUEST_ID_NONE) {
451
452 int requestId = mRepeatingRequestId;
453 mRepeatingRequestId = REQUEST_ID_NONE;
454
455 // Queue for deletion after in-flight requests finish
Zhijun He1a9b6462014-03-31 16:11:33 -0700456 if (mCaptureListenerMap.get(requestId) != null) {
457 mRepeatingRequestIdDeletedList.add(requestId);
458 }
Igor Murashkin70725502013-06-25 20:27:06 +0000459
460 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700461 LongParcelable lastFrameNumberRef = new LongParcelable();
462 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
463 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700464
465 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
466
Igor Murashkin70725502013-06-25 20:27:06 +0000467 } catch (CameraRuntimeException e) {
468 throw e.asChecked();
469 } catch (RemoteException e) {
470 // impossible
471 return;
472 }
473 }
474 }
475 }
476
Zhijun Hed842fcd2013-12-26 14:14:04 -0800477 private void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700478
479 synchronized (mLock) {
480 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700481 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Zhijun He7f4d3142013-07-23 07:54:38 -0700482 throw new IllegalStateException("Active repeating request ongoing");
483 }
484
485 try {
486 mRemoteDevice.waitUntilIdle();
487 } catch (CameraRuntimeException e) {
488 throw e.asChecked();
489 } catch (RemoteException e) {
490 // impossible
491 return;
492 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700493
494 mRepeatingRequestId = REQUEST_ID_NONE;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700495 }
Igor Murashkin70725502013-06-25 20:27:06 +0000496 }
497
498 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700499 public void flush() throws CameraAccessException {
500 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700501 checkIfCameraClosed();
502
503 mDeviceHandler.post(mCallOnBusy);
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700504 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700505 LongParcelable lastFrameNumberRef = new LongParcelable();
506 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
507 if (mRepeatingRequestId != REQUEST_ID_NONE) {
508 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700509 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700510 mRepeatingRequestId = REQUEST_ID_NONE;
511 }
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700512 } catch (CameraRuntimeException e) {
513 throw e.asChecked();
514 } catch (RemoteException e) {
515 // impossible
516 return;
517 }
518 }
519 }
520
521 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700522 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000523 synchronized (mLock) {
524
525 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700526 if (mRemoteDevice != null) {
527 mRemoteDevice.disconnect();
528 }
Igor Murashkin70725502013-06-25 20:27:06 +0000529 } catch (CameraRuntimeException e) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700530 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000531 } catch (RemoteException e) {
532 // impossible
533 }
534
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700535 if (mRemoteDevice != null) {
536 mDeviceHandler.post(mCallOnClosed);
537 }
Igor Murashkin70725502013-06-25 20:27:06 +0000538
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700539 mRemoteDevice = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000540 }
541 }
542
543 @Override
544 protected void finalize() throws Throwable {
545 try {
546 close();
Igor Murashkin70725502013-06-25 20:27:06 +0000547 }
548 finally {
549 super.finalize();
550 }
551 }
552
553 static class CaptureListenerHolder {
554
555 private final boolean mRepeating;
556 private final CaptureListener mListener;
Jianing Weid2c3a822014-03-27 18:27:43 -0700557 private final List<CaptureRequest> mRequestList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700558 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000559
Jianing Weid2c3a822014-03-27 18:27:43 -0700560 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
561 Handler handler, boolean repeating) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700562 if (listener == null || handler == null) {
563 throw new UnsupportedOperationException(
564 "Must have a valid handler and a valid listener");
565 }
Igor Murashkin70725502013-06-25 20:27:06 +0000566 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700567 mHandler = handler;
Jianing Weid2c3a822014-03-27 18:27:43 -0700568 mRequestList = new ArrayList<CaptureRequest>(requestList);
Igor Murashkin70725502013-06-25 20:27:06 +0000569 mListener = listener;
570 }
571
572 public boolean isRepeating() {
573 return mRepeating;
574 }
575
576 public CaptureListener getListener() {
577 return mListener;
578 }
579
Jianing Weid2c3a822014-03-27 18:27:43 -0700580 public CaptureRequest getRequest(int subsequenceId) {
581 if (subsequenceId >= mRequestList.size()) {
582 throw new IllegalArgumentException(
583 String.format(
584 "Requested subsequenceId %d is larger than request list size %d.",
585 subsequenceId, mRequestList.size()));
586 } else {
587 if (subsequenceId < 0) {
588 throw new IllegalArgumentException(String.format(
589 "Requested subsequenceId %d is negative", subsequenceId));
590 } else {
591 return mRequestList.get(subsequenceId);
592 }
593 }
594 }
595
Igor Murashkin70725502013-06-25 20:27:06 +0000596 public CaptureRequest getRequest() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700597 return getRequest(0);
Igor Murashkin70725502013-06-25 20:27:06 +0000598 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700599
600 public Handler getHandler() {
601 return mHandler;
602 }
603
Igor Murashkin70725502013-06-25 20:27:06 +0000604 }
605
Jianing Weid2c3a822014-03-27 18:27:43 -0700606 /**
607 * This class tracks the last frame number for submitted requests.
608 */
609 public class FrameNumberTracker {
610
611 private long mCompletedFrameNumber = -1;
612 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
613
614 private void update() {
615 Iterator<Long> iter = mFutureErrorSet.iterator();
616 while (iter.hasNext()) {
617 long errorFrameNumber = iter.next();
618 if (errorFrameNumber == mCompletedFrameNumber + 1) {
619 mCompletedFrameNumber++;
620 iter.remove();
621 } else {
622 break;
623 }
624 }
625 }
626
627 /**
628 * This function is called every time when a result or an error is received.
629 * @param frameNumber: the frame number corresponding to the result or error
630 * @param isError: true if it is an error, false if it is not an error
631 */
632 public void updateTracker(long frameNumber, boolean isError) {
633 if (isError) {
634 mFutureErrorSet.add(frameNumber);
635 } else {
636 /**
637 * HAL cannot send an OnResultReceived for frame N unless it knows for
638 * sure that all frames prior to N have either errored out or completed.
639 * So if the current frame is not an error, then all previous frames
640 * should have arrived. The following line checks whether this holds.
641 */
642 if (frameNumber != mCompletedFrameNumber + 1) {
Zhijun He22589b42014-05-01 10:37:36 -0700643 Log.e(TAG, String.format(
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700644 "result frame number %d comes out of order, should be %d + 1",
645 frameNumber, mCompletedFrameNumber));
Jianing Weid2c3a822014-03-27 18:27:43 -0700646 }
647 mCompletedFrameNumber++;
648 }
649 update();
650 }
651
652 public long getCompletedFrameNumber() {
653 return mCompletedFrameNumber;
654 }
655
656 }
657
658 private void checkAndFireSequenceComplete() {
659 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
660 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
661 while (iter.hasNext()) {
662 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
663 if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
664
665 // remove request from mCaptureListenerMap
666 final int requestId = frameNumberRequestPair.getValue();
667 final CaptureListenerHolder holder;
668 synchronized (mLock) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700669 int index = mCaptureListenerMap.indexOfKey(requestId);
670 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
Jianing Weid2c3a822014-03-27 18:27:43 -0700671 : null;
672 if (holder != null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700673 mCaptureListenerMap.removeAt(index);
674 if (DEBUG) {
675 Log.v(TAG, String.format(
676 "remove holder for requestId %d, "
677 + "because lastFrame %d is <= %d",
678 requestId, frameNumberRequestPair.getKey(),
679 completedFrameNumber));
680 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700681 }
682 }
683 iter.remove();
684
685 // Call onCaptureSequenceCompleted
686 if (holder != null) {
687 Runnable resultDispatch = new Runnable() {
688 @Override
689 public void run() {
690 if (!CameraDevice.this.isClosed()){
691 if (DEBUG) {
692 Log.d(TAG, String.format(
693 "fire sequence complete for request %d",
694 requestId));
695 }
696
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700697 long lastFrameNumber = frameNumberRequestPair.getKey();
698 if (lastFrameNumber < Integer.MIN_VALUE
699 || lastFrameNumber > Integer.MAX_VALUE) {
700 throw new AssertionError(lastFrameNumber
701 + " cannot be cast to int");
702 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700703 holder.getListener().onCaptureSequenceCompleted(
704 CameraDevice.this,
705 requestId,
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700706 (int)lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700707 }
708 }
709 };
710 holder.getHandler().post(resultDispatch);
711 }
712
713 }
714 }
715 }
716
Zhijun Heecb323e2013-07-31 09:40:27 -0700717 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000718
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700719 //
720 // Constants below need to be kept up-to-date with
721 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
722 //
723
724 //
725 // Error codes for onCameraError
726 //
727
728 /**
729 * Camera has been disconnected
730 */
731 static final int ERROR_CAMERA_DISCONNECTED = 0;
732
733 /**
734 * Camera has encountered a device-level error
735 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
736 */
737 static final int ERROR_CAMERA_DEVICE = 1;
738
739 /**
740 * Camera has encountered a service-level error
741 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
742 */
743 static final int ERROR_CAMERA_SERVICE = 2;
744
Igor Murashkin70725502013-06-25 20:27:06 +0000745 @Override
746 public IBinder asBinder() {
747 return this;
748 }
749
Igor Murashkin70725502013-06-25 20:27:06 +0000750 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700751 public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700752 Runnable r = null;
753 if (isClosed()) return;
754
755 synchronized(mLock) {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700756 switch (errorCode) {
757 case ERROR_CAMERA_DISCONNECTED:
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700758 r = mCallOnDisconnected;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700759 break;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700760 default:
761 Log.e(TAG, "Unknown error from camera device: " + errorCode);
762 // no break
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700763 case ERROR_CAMERA_DEVICE:
764 case ERROR_CAMERA_SERVICE:
765 r = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700766 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700767 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700768 if (!CameraDevice.this.isClosed()) {
769 mDeviceListener.onError(CameraDevice.this, errorCode);
770 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700771 }
772 };
773 break;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700774 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700775 CameraDevice.this.mDeviceHandler.post(r);
Zhijun Heecb323e2013-07-31 09:40:27 -0700776 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700777
778 // Fire onCaptureSequenceCompleted
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700779 if (DEBUG) {
780 Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
781 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700782 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
783 checkAndFireSequenceComplete();
784
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700785 }
786
787 @Override
788 public void onCameraIdle() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700789 if (isClosed()) return;
790
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700791 if (DEBUG) {
792 Log.d(TAG, "Camera now idle");
793 }
794 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700795 if (!CameraDevice.this.mIdle) {
796 CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
797 }
798 CameraDevice.this.mIdle = true;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700799 }
800 }
801
802 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700803 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
804 int requestId = resultExtras.getRequestId();
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700805 if (DEBUG) {
806 Log.d(TAG, "Capture started for id " + requestId);
807 }
808 final CaptureListenerHolder holder;
809
810 // Get the listener for this frame ID, if there is one
811 synchronized (mLock) {
812 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
813 }
814
815 if (holder == null) {
816 return;
817 }
818
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700819 if (isClosed()) return;
820
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700821 // Dispatch capture start notice
822 holder.getHandler().post(
823 new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700824 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700825 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700826 if (!CameraDevice.this.isClosed()) {
827 holder.getListener().onCaptureStarted(
828 CameraDevice.this,
Jianing Weid2c3a822014-03-27 18:27:43 -0700829 holder.getRequest(resultExtras.getSubsequenceId()),
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700830 timestamp);
831 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700832 }
833 });
Igor Murashkin70725502013-06-25 20:27:06 +0000834 }
835
836 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700837 public void onResultReceived(CameraMetadataNative result,
838 CaptureResultExtras resultExtras) throws RemoteException {
839 int requestId = resultExtras.getRequestId();
Zhijun Heecb323e2013-07-31 09:40:27 -0700840 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700841 Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
842 + requestId);
Zhijun Heecb323e2013-07-31 09:40:27 -0700843 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700844 final CaptureListenerHolder holder;
845 synchronized (mLock) {
846 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
847 }
Igor Murashkin70725502013-06-25 20:27:06 +0000848
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800849 Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
850 boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
851
Jianing Weibaf0c652014-04-18 17:35:00 -0700852 // Update tracker (increment counter) when it's not a partial result.
853 if (!quirkIsPartialResult) {
854 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
855 }
856
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700857 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000858 if (holder == null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700859 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700860 Log.d(TAG,
861 "holder is null, early return at frame "
862 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700863 }
Igor Murashkin70725502013-06-25 20:27:06 +0000864 return;
865 }
866
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700867 if (isClosed()) {
868 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700869 Log.d(TAG,
870 "camera is closed, early return at frame "
871 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700872 }
873 return;
874 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700875
Jianing Weid2c3a822014-03-27 18:27:43 -0700876 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700877 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000878
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800879 Runnable resultDispatch = null;
880
881 // Either send a partial result or the final capture completed result
882 if (quirkIsPartialResult) {
883 // Partial result
884 resultDispatch = new Runnable() {
885 @Override
886 public void run() {
887 if (!CameraDevice.this.isClosed()){
888 holder.getListener().onCapturePartial(
889 CameraDevice.this,
890 request,
891 resultAsCapture);
892 }
893 }
894 };
895 } else {
896 // Final capture result
897 resultDispatch = new Runnable() {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700898 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700899 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700900 if (!CameraDevice.this.isClosed()){
901 holder.getListener().onCaptureCompleted(
902 CameraDevice.this,
903 request,
904 resultAsCapture);
905 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700906 }
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800907 };
908 }
909
910 holder.getHandler().post(resultDispatch);
Jianing Weid2c3a822014-03-27 18:27:43 -0700911
912 // Fire onCaptureSequenceCompleted
913 if (!quirkIsPartialResult) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700914 checkAndFireSequenceComplete();
915 }
Igor Murashkin70725502013-06-25 20:27:06 +0000916 }
917
918 }
919
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700920 /**
921 * Default handler management. If handler is null, get the current thread's
922 * Looper to create a Handler with. If no looper exists, throw exception.
923 */
924 private Handler checkHandler(Handler handler) {
925 if (handler == null) {
926 Looper looper = Looper.myLooper();
927 if (looper == null) {
928 throw new IllegalArgumentException(
929 "No handler given, and current thread has no looper!");
930 }
931 handler = new Handler(looper);
932 }
933 return handler;
934 }
935
Zhijun He7f4d3142013-07-23 07:54:38 -0700936 private void checkIfCameraClosed() {
937 if (mRemoteDevice == null) {
938 throw new IllegalStateException("CameraDevice was already closed");
939 }
940 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700941
942 private boolean isClosed() {
943 synchronized(mLock) {
944 return (mRemoteDevice == null);
945 }
946 }
Igor Murashkin70725502013-06-25 20:27:06 +0000947}