blob: 7b249766a977318dbe210f08d9151b5aeaeacf37 [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 Talvalacca00c62014-05-14 10:53:20 -070022import android.hardware.camera2.CameraCaptureSession;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070023import android.hardware.camera2.CaptureRequest;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070024import android.hardware.camera2.CaptureResult;
25import android.hardware.camera2.ICameraDeviceCallbacks;
26import android.hardware.camera2.ICameraDeviceUser;
Igor Murashkindb075af2014-05-21 10:07:08 -070027import android.hardware.camera2.TotalCaptureResult;
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;
Igor Murashkin9c595172014-05-12 13:56:20 -070030import android.hardware.camera2.utils.LongParcelable;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070031import android.os.Handler;
Ruben Brunkdecfe952013-10-29 11:00:32 -070032import android.os.IBinder;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070033import android.os.Looper;
Ruben Brunkdecfe952013-10-29 11:00:32 -070034import android.os.RemoteException;
Igor Murashkin70725502013-06-25 20:27:06 +000035import android.util.Log;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070036import android.util.SparseArray;
Igor Murashkin70725502013-06-25 20:27:06 +000037import android.view.Surface;
38
Jianing Weid2c3a822014-03-27 18:27:43 -070039import java.util.AbstractMap.SimpleEntry;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070040import java.util.ArrayList;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070041import java.util.HashSet;
Ruben Brunkdecfe952013-10-29 11:00:32 -070042import java.util.Iterator;
Igor Murashkin70725502013-06-25 20:27:06 +000043import java.util.List;
Jianing Weid2c3a822014-03-27 18:27:43 -070044import java.util.TreeSet;
Igor Murashkin70725502013-06-25 20:27:06 +000045
46/**
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070047 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
Igor Murashkin70725502013-06-25 20:27:06 +000048 */
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070049public class CameraDevice implements android.hardware.camera2.CameraDevice {
Igor Murashkin70725502013-06-25 20:27:06 +000050
51 private final String TAG;
Zhijun Heecb323e2013-07-31 09:40:27 -070052 private final boolean DEBUG;
Igor Murashkin70725502013-06-25 20:27:06 +000053
Ruben Brunkdecfe952013-10-29 11:00:32 -070054 private static final int REQUEST_ID_NONE = -1;
55
Igor Murashkin70725502013-06-25 20:27:06 +000056 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
57 private ICameraDeviceUser mRemoteDevice;
58
59 private final Object mLock = new Object();
60 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
61
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070062 private final StateListener mDeviceListener;
63 private final Handler mDeviceHandler;
64
65 private boolean mIdle = true;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070066
67 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
68 new SparseArray<CaptureListenerHolder>();
Igor Murashkin70725502013-06-25 20:27:06 +000069
Ruben Brunkdecfe952013-10-29 11:00:32 -070070 private int mRepeatingRequestId = REQUEST_ID_NONE;
71 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
Igor Murashkin57ea59b2013-08-23 16:55:57 -070072 // Map stream IDs to Surfaces
73 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
Igor Murashkin70725502013-06-25 20:27:06 +000074
75 private final String mCameraId;
76
Jianing Weid2c3a822014-03-27 18:27:43 -070077 /**
78 * A list tracking request and its expected last frame.
79 * Updated when calling ICameraDeviceUser methods.
80 */
81 private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
82 mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
83
84 /**
85 * An object tracking received frame numbers.
86 * Updated when receiving callbacks from ICameraDeviceCallbacks.
87 */
88 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
89
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070090 // Runnables for all state transitions, except error, which needs the
91 // error code argument
92
93 private final Runnable mCallOnOpened = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -070094 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070095 public void run() {
96 if (!CameraDevice.this.isClosed()) {
97 mDeviceListener.onOpened(CameraDevice.this);
98 }
99 }
100 };
101
102 private final Runnable mCallOnUnconfigured = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700103 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700104 public void run() {
105 if (!CameraDevice.this.isClosed()) {
106 mDeviceListener.onUnconfigured(CameraDevice.this);
107 }
108 }
109 };
110
111 private final Runnable mCallOnActive = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700112 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700113 public void run() {
114 if (!CameraDevice.this.isClosed()) {
115 mDeviceListener.onActive(CameraDevice.this);
116 }
117 }
118 };
119
120 private final Runnable mCallOnBusy = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700121 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700122 public void run() {
123 if (!CameraDevice.this.isClosed()) {
124 mDeviceListener.onBusy(CameraDevice.this);
125 }
126 }
127 };
128
129 private final Runnable mCallOnClosed = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700130 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700131 public void run() {
Zhijun He2db56022014-01-02 15:12:00 -0800132 mDeviceListener.onClosed(CameraDevice.this);
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700133 }
134 };
135
136 private final Runnable mCallOnIdle = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700137 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700138 public void run() {
139 if (!CameraDevice.this.isClosed()) {
140 mDeviceListener.onIdle(CameraDevice.this);
141 }
142 }
143 };
144
145 private final Runnable mCallOnDisconnected = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700146 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700147 public void run() {
148 if (!CameraDevice.this.isClosed()) {
149 mDeviceListener.onDisconnected(CameraDevice.this);
150 }
151 }
152 };
153
154 public CameraDevice(String cameraId, StateListener listener, Handler handler) {
155 if (cameraId == null || listener == null || handler == null) {
156 throw new IllegalArgumentException("Null argument given");
157 }
Igor Murashkin70725502013-06-25 20:27:06 +0000158 mCameraId = cameraId;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700159 mDeviceListener = listener;
160 mDeviceHandler = handler;
Eino-Ville Talvalab72d1ef2014-04-28 13:21:18 -0700161
162 final int MAX_TAG_LEN = 23;
163 String tag = String.format("CameraDevice-JV-%s", mCameraId);
164 if (tag.length() > MAX_TAG_LEN) {
165 tag = tag.substring(0, MAX_TAG_LEN);
166 }
167 TAG = tag;
168
Zhijun Heecb323e2013-07-31 09:40:27 -0700169 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +0000170 }
171
172 public CameraDeviceCallbacks getCallbacks() {
173 return mCallbacks;
174 }
175
Igor Murashkin70725502013-06-25 20:27:06 +0000176 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -0700177 // TODO: Move from decorator to direct binder-mediated exceptions
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700178 synchronized(mLock) {
179 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
180
181 mDeviceHandler.post(mCallOnOpened);
182 mDeviceHandler.post(mCallOnUnconfigured);
183 }
Igor Murashkin70725502013-06-25 20:27:06 +0000184 }
185
186 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700187 public String getId() {
188 return mCameraId;
189 }
190
191 @Override
Igor Murashkin70725502013-06-25 20:27:06 +0000192 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700193 // Treat a null input the same an empty list
194 if (outputs == null) {
195 outputs = new ArrayList<Surface>();
196 }
Igor Murashkin70725502013-06-25 20:27:06 +0000197 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700198 checkIfCameraClosed();
199
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700200 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
201 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
202
203 // Determine which streams need to be created, which to be deleted
204 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
205 int streamId = mConfiguredOutputs.keyAt(i);
206 Surface s = mConfiguredOutputs.valueAt(i);
207
208 if (!outputs.contains(s)) {
209 deleteList.add(streamId);
210 } else {
211 addSet.remove(s); // Don't create a stream previously created
212 }
213 }
214
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700215 mDeviceHandler.post(mCallOnBusy);
216 stopRepeating();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700217
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700218 try {
Ruben Brunkdecfe952013-10-29 11:00:32 -0700219 waitUntilIdle();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700220
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700221 mRemoteDevice.beginConfigure();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700222 // Delete all streams first (to free up HW resources)
223 for (Integer streamId : deleteList) {
224 mRemoteDevice.deleteStream(streamId);
225 mConfiguredOutputs.delete(streamId);
226 }
227
228 // Add all new streams
229 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000230 // TODO: remove width,height,format since we are ignoring
231 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700232 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
233 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000234 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700235
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700236 mRemoteDevice.endConfigure();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700237 } catch (CameraRuntimeException e) {
238 if (e.getReason() == CAMERA_IN_USE) {
239 throw new IllegalStateException("The camera is currently busy." +
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700240 " You must wait until the previous operation completes.");
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700241 }
242
243 throw e.asChecked();
244 } catch (RemoteException e) {
245 // impossible
246 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000247 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700248
249 if (outputs.size() > 0) {
250 mDeviceHandler.post(mCallOnIdle);
251 } else {
252 mDeviceHandler.post(mCallOnUnconfigured);
253 }
Igor Murashkin70725502013-06-25 20:27:06 +0000254 }
255 }
256
257 @Override
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700258 public void createCaptureSession(List<Surface> outputs,
259 CameraCaptureSession.StateListener listener, Handler handler)
260 throws CameraAccessException {
261 // TODO
262 }
263
264 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700265 public CaptureRequest.Builder createCaptureRequest(int templateType)
266 throws CameraAccessException {
Igor Murashkin70725502013-06-25 20:27:06 +0000267 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700268 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000269
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700270 CameraMetadataNative templatedRequest = new CameraMetadataNative();
Igor Murashkin70725502013-06-25 20:27:06 +0000271
272 try {
273 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
274 } catch (CameraRuntimeException e) {
275 throw e.asChecked();
276 } catch (RemoteException e) {
277 // impossible
278 return null;
279 }
280
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700281 CaptureRequest.Builder builder =
282 new CaptureRequest.Builder(templatedRequest);
Igor Murashkin70725502013-06-25 20:27:06 +0000283
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700284 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000285 }
286 }
287
288 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700289 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000290 throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700291 if (DEBUG) {
292 Log.d(TAG, "calling capture");
293 }
294 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
295 requestList.add(request);
296 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000297 }
298
299 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700300 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700301 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700302 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700303 if (requests.isEmpty()) {
304 Log.w(TAG, "Capture burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700305 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700306 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700307 return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000308 }
309
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700310 /**
311 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
312 * starting and stopping repeating request and flushing.
313 *
314 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
315 * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
316 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
317 * is added to the list mFrameNumberRequestPairs.</p>
318 *
319 * @param requestId the request ID of the current repeating request.
320 *
321 * @param lastFrameNumber last frame number returned from binder.
322 */
323 private void checkEarlyTriggerSequenceComplete(
324 final int requestId, final long lastFrameNumber) {
325 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
326 // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
327 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
328 final CaptureListenerHolder holder;
329 int index = mCaptureListenerMap.indexOfKey(requestId);
330 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
331 if (holder != null) {
332 mCaptureListenerMap.removeAt(index);
Jianing Weibaf0c652014-04-18 17:35:00 -0700333 if (DEBUG) {
334 Log.v(TAG, String.format(
335 "remove holder for requestId %d, "
336 + "because lastFrame is %d.",
337 requestId, lastFrameNumber));
338 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700339 }
340
341 if (holder != null) {
342 if (DEBUG) {
343 Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
344 + " request did not reach HAL");
345 }
346
347 Runnable resultDispatch = new Runnable() {
348 @Override
349 public void run() {
350 if (!CameraDevice.this.isClosed()) {
351 if (DEBUG) {
352 Log.d(TAG, String.format(
353 "early trigger sequence complete for request %d",
354 requestId));
355 }
356 if (lastFrameNumber < Integer.MIN_VALUE
357 || lastFrameNumber > Integer.MAX_VALUE) {
358 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
359 }
360 holder.getListener().onCaptureSequenceCompleted(
361 CameraDevice.this,
362 requestId,
Igor Murashkindb075af2014-05-21 10:07:08 -0700363 lastFrameNumber);
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700364 }
365 }
366 };
367 holder.getHandler().post(resultDispatch);
368 } else {
369 Log.w(TAG, String.format(
370 "did not register listener to request %d",
371 requestId));
372 }
373 } else {
374 mFrameNumberRequestPairs.add(
375 new SimpleEntry<Long, Integer>(lastFrameNumber,
376 requestId));
377 }
378 }
379
Jianing Weid2c3a822014-03-27 18:27:43 -0700380 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700381 Handler handler, boolean repeating) throws CameraAccessException {
382
383 // Need a valid handler, or current thread needs to have a looper, if
384 // listener is valid
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700385 if (listener != null) {
386 handler = checkHandler(handler);
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700387 }
Igor Murashkin70725502013-06-25 20:27:06 +0000388
389 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700390 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000391 int requestId;
392
Ruben Brunke73b41b2013-11-07 19:30:43 -0800393 if (repeating) {
394 stopRepeating();
395 }
396
Jianing Weid2c3a822014-03-27 18:27:43 -0700397 LongParcelable lastFrameNumberRef = new LongParcelable();
Igor Murashkin70725502013-06-25 20:27:06 +0000398 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700399 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
400 /*out*/lastFrameNumberRef);
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700401 if (DEBUG) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700402 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
403 }
Igor Murashkin70725502013-06-25 20:27:06 +0000404 } catch (CameraRuntimeException e) {
405 throw e.asChecked();
406 } catch (RemoteException e) {
407 // impossible
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700408 return -1;
Igor Murashkin70725502013-06-25 20:27:06 +0000409 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700410
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700411 if (listener != null) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700412 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
413 requestList, handler, repeating));
Jianing Weibaf0c652014-04-18 17:35:00 -0700414 } else {
415 if (DEBUG) {
416 Log.d(TAG, "Listen for request " + requestId + " is null");
417 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700418 }
Igor Murashkin70725502013-06-25 20:27:06 +0000419
Jianing Weid2c3a822014-03-27 18:27:43 -0700420 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700421
Igor Murashkin70725502013-06-25 20:27:06 +0000422 if (repeating) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700423 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700424 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700425 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700426 mRepeatingRequestId = requestId;
Jianing Weid2c3a822014-03-27 18:27:43 -0700427 } else {
428 mFrameNumberRequestPairs.add(
429 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
Igor Murashkin70725502013-06-25 20:27:06 +0000430 }
431
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700432 if (mIdle) {
433 mDeviceHandler.post(mCallOnActive);
434 }
435 mIdle = false;
436
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700437 return requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000438 }
439 }
440
441 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700442 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700443 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700444 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
445 requestList.add(request);
446 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000447 }
448
449 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700450 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700451 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700452 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700453 if (requests.isEmpty()) {
454 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700455 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700456 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700457 return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000458 }
459
460 @Override
461 public void stopRepeating() throws CameraAccessException {
462
463 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700464 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700465 if (mRepeatingRequestId != REQUEST_ID_NONE) {
466
467 int requestId = mRepeatingRequestId;
468 mRepeatingRequestId = REQUEST_ID_NONE;
469
470 // Queue for deletion after in-flight requests finish
Zhijun He1a9b6462014-03-31 16:11:33 -0700471 if (mCaptureListenerMap.get(requestId) != null) {
472 mRepeatingRequestIdDeletedList.add(requestId);
473 }
Igor Murashkin70725502013-06-25 20:27:06 +0000474
475 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700476 LongParcelable lastFrameNumberRef = new LongParcelable();
477 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
478 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700479
480 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
481
Igor Murashkin70725502013-06-25 20:27:06 +0000482 } catch (CameraRuntimeException e) {
483 throw e.asChecked();
484 } catch (RemoteException e) {
485 // impossible
486 return;
487 }
488 }
489 }
490 }
491
Zhijun Hed842fcd2013-12-26 14:14:04 -0800492 private void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700493
494 synchronized (mLock) {
495 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700496 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Zhijun He7f4d3142013-07-23 07:54:38 -0700497 throw new IllegalStateException("Active repeating request ongoing");
498 }
499
500 try {
501 mRemoteDevice.waitUntilIdle();
502 } catch (CameraRuntimeException e) {
503 throw e.asChecked();
504 } catch (RemoteException e) {
505 // impossible
506 return;
507 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700508
509 mRepeatingRequestId = REQUEST_ID_NONE;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700510 }
Igor Murashkin70725502013-06-25 20:27:06 +0000511 }
512
513 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700514 public void flush() throws CameraAccessException {
515 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700516 checkIfCameraClosed();
517
518 mDeviceHandler.post(mCallOnBusy);
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700519 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700520 LongParcelable lastFrameNumberRef = new LongParcelable();
521 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
522 if (mRepeatingRequestId != REQUEST_ID_NONE) {
523 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700524 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700525 mRepeatingRequestId = REQUEST_ID_NONE;
526 }
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700527 } catch (CameraRuntimeException e) {
528 throw e.asChecked();
529 } catch (RemoteException e) {
530 // impossible
531 return;
532 }
533 }
534 }
535
536 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700537 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000538 synchronized (mLock) {
539
540 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700541 if (mRemoteDevice != null) {
542 mRemoteDevice.disconnect();
543 }
Igor Murashkin70725502013-06-25 20:27:06 +0000544 } catch (CameraRuntimeException e) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700545 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000546 } catch (RemoteException e) {
547 // impossible
548 }
549
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700550 if (mRemoteDevice != null) {
551 mDeviceHandler.post(mCallOnClosed);
552 }
Igor Murashkin70725502013-06-25 20:27:06 +0000553
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700554 mRemoteDevice = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000555 }
556 }
557
558 @Override
559 protected void finalize() throws Throwable {
560 try {
561 close();
Igor Murashkin70725502013-06-25 20:27:06 +0000562 }
563 finally {
564 super.finalize();
565 }
566 }
567
568 static class CaptureListenerHolder {
569
570 private final boolean mRepeating;
571 private final CaptureListener mListener;
Jianing Weid2c3a822014-03-27 18:27:43 -0700572 private final List<CaptureRequest> mRequestList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700573 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000574
Jianing Weid2c3a822014-03-27 18:27:43 -0700575 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
576 Handler handler, boolean repeating) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700577 if (listener == null || handler == null) {
578 throw new UnsupportedOperationException(
579 "Must have a valid handler and a valid listener");
580 }
Igor Murashkin70725502013-06-25 20:27:06 +0000581 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700582 mHandler = handler;
Jianing Weid2c3a822014-03-27 18:27:43 -0700583 mRequestList = new ArrayList<CaptureRequest>(requestList);
Igor Murashkin70725502013-06-25 20:27:06 +0000584 mListener = listener;
585 }
586
587 public boolean isRepeating() {
588 return mRepeating;
589 }
590
591 public CaptureListener getListener() {
592 return mListener;
593 }
594
Jianing Weid2c3a822014-03-27 18:27:43 -0700595 public CaptureRequest getRequest(int subsequenceId) {
596 if (subsequenceId >= mRequestList.size()) {
597 throw new IllegalArgumentException(
598 String.format(
599 "Requested subsequenceId %d is larger than request list size %d.",
600 subsequenceId, mRequestList.size()));
601 } else {
602 if (subsequenceId < 0) {
603 throw new IllegalArgumentException(String.format(
604 "Requested subsequenceId %d is negative", subsequenceId));
605 } else {
606 return mRequestList.get(subsequenceId);
607 }
608 }
609 }
610
Igor Murashkin70725502013-06-25 20:27:06 +0000611 public CaptureRequest getRequest() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700612 return getRequest(0);
Igor Murashkin70725502013-06-25 20:27:06 +0000613 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700614
615 public Handler getHandler() {
616 return mHandler;
617 }
618
Igor Murashkin70725502013-06-25 20:27:06 +0000619 }
620
Jianing Weid2c3a822014-03-27 18:27:43 -0700621 /**
622 * This class tracks the last frame number for submitted requests.
623 */
624 public class FrameNumberTracker {
625
626 private long mCompletedFrameNumber = -1;
627 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
628
629 private void update() {
630 Iterator<Long> iter = mFutureErrorSet.iterator();
631 while (iter.hasNext()) {
632 long errorFrameNumber = iter.next();
633 if (errorFrameNumber == mCompletedFrameNumber + 1) {
634 mCompletedFrameNumber++;
635 iter.remove();
636 } else {
637 break;
638 }
639 }
640 }
641
642 /**
643 * This function is called every time when a result or an error is received.
644 * @param frameNumber: the frame number corresponding to the result or error
645 * @param isError: true if it is an error, false if it is not an error
646 */
647 public void updateTracker(long frameNumber, boolean isError) {
648 if (isError) {
649 mFutureErrorSet.add(frameNumber);
650 } else {
651 /**
652 * HAL cannot send an OnResultReceived for frame N unless it knows for
653 * sure that all frames prior to N have either errored out or completed.
654 * So if the current frame is not an error, then all previous frames
655 * should have arrived. The following line checks whether this holds.
656 */
657 if (frameNumber != mCompletedFrameNumber + 1) {
Zhijun He22589b42014-05-01 10:37:36 -0700658 Log.e(TAG, String.format(
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700659 "result frame number %d comes out of order, should be %d + 1",
660 frameNumber, mCompletedFrameNumber));
Jianing Weid2c3a822014-03-27 18:27:43 -0700661 }
662 mCompletedFrameNumber++;
663 }
664 update();
665 }
666
667 public long getCompletedFrameNumber() {
668 return mCompletedFrameNumber;
669 }
670
671 }
672
673 private void checkAndFireSequenceComplete() {
674 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
675 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
676 while (iter.hasNext()) {
677 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
678 if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
679
680 // remove request from mCaptureListenerMap
681 final int requestId = frameNumberRequestPair.getValue();
682 final CaptureListenerHolder holder;
683 synchronized (mLock) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700684 int index = mCaptureListenerMap.indexOfKey(requestId);
685 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
Jianing Weid2c3a822014-03-27 18:27:43 -0700686 : null;
687 if (holder != null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700688 mCaptureListenerMap.removeAt(index);
689 if (DEBUG) {
690 Log.v(TAG, String.format(
691 "remove holder for requestId %d, "
692 + "because lastFrame %d is <= %d",
693 requestId, frameNumberRequestPair.getKey(),
694 completedFrameNumber));
695 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700696 }
697 }
698 iter.remove();
699
700 // Call onCaptureSequenceCompleted
701 if (holder != null) {
702 Runnable resultDispatch = new Runnable() {
703 @Override
704 public void run() {
705 if (!CameraDevice.this.isClosed()){
706 if (DEBUG) {
707 Log.d(TAG, String.format(
708 "fire sequence complete for request %d",
709 requestId));
710 }
711
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700712 long lastFrameNumber = frameNumberRequestPair.getKey();
713 if (lastFrameNumber < Integer.MIN_VALUE
714 || lastFrameNumber > Integer.MAX_VALUE) {
715 throw new AssertionError(lastFrameNumber
716 + " cannot be cast to int");
717 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700718 holder.getListener().onCaptureSequenceCompleted(
719 CameraDevice.this,
720 requestId,
Igor Murashkindb075af2014-05-21 10:07:08 -0700721 lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700722 }
723 }
724 };
725 holder.getHandler().post(resultDispatch);
726 }
727
728 }
729 }
730 }
731
Zhijun Heecb323e2013-07-31 09:40:27 -0700732 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000733
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700734 //
735 // Constants below need to be kept up-to-date with
736 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
737 //
738
739 //
740 // Error codes for onCameraError
741 //
742
743 /**
744 * Camera has been disconnected
745 */
746 static final int ERROR_CAMERA_DISCONNECTED = 0;
747
748 /**
749 * Camera has encountered a device-level error
750 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
751 */
752 static final int ERROR_CAMERA_DEVICE = 1;
753
754 /**
755 * Camera has encountered a service-level error
756 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
757 */
758 static final int ERROR_CAMERA_SERVICE = 2;
759
Igor Murashkin70725502013-06-25 20:27:06 +0000760 @Override
761 public IBinder asBinder() {
762 return this;
763 }
764
Igor Murashkin70725502013-06-25 20:27:06 +0000765 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700766 public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700767 Runnable r = null;
768 if (isClosed()) return;
769
770 synchronized(mLock) {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700771 switch (errorCode) {
772 case ERROR_CAMERA_DISCONNECTED:
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700773 r = mCallOnDisconnected;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700774 break;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700775 default:
776 Log.e(TAG, "Unknown error from camera device: " + errorCode);
777 // no break
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700778 case ERROR_CAMERA_DEVICE:
779 case ERROR_CAMERA_SERVICE:
780 r = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700781 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700782 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700783 if (!CameraDevice.this.isClosed()) {
784 mDeviceListener.onError(CameraDevice.this, errorCode);
785 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700786 }
787 };
788 break;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700789 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700790 CameraDevice.this.mDeviceHandler.post(r);
Zhijun Heecb323e2013-07-31 09:40:27 -0700791 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700792
793 // Fire onCaptureSequenceCompleted
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700794 if (DEBUG) {
795 Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
796 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700797 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
798 checkAndFireSequenceComplete();
799
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700800 }
801
802 @Override
803 public void onCameraIdle() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700804 if (isClosed()) return;
805
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700806 if (DEBUG) {
807 Log.d(TAG, "Camera now idle");
808 }
809 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700810 if (!CameraDevice.this.mIdle) {
811 CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
812 }
813 CameraDevice.this.mIdle = true;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700814 }
815 }
816
817 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700818 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
819 int requestId = resultExtras.getRequestId();
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700820 if (DEBUG) {
821 Log.d(TAG, "Capture started for id " + requestId);
822 }
823 final CaptureListenerHolder holder;
824
825 // Get the listener for this frame ID, if there is one
826 synchronized (mLock) {
827 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
828 }
829
830 if (holder == null) {
831 return;
832 }
833
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700834 if (isClosed()) return;
835
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700836 // Dispatch capture start notice
837 holder.getHandler().post(
838 new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700839 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700840 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700841 if (!CameraDevice.this.isClosed()) {
842 holder.getListener().onCaptureStarted(
843 CameraDevice.this,
Jianing Weid2c3a822014-03-27 18:27:43 -0700844 holder.getRequest(resultExtras.getSubsequenceId()),
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700845 timestamp);
846 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700847 }
848 });
Igor Murashkin70725502013-06-25 20:27:06 +0000849 }
850
851 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700852 public void onResultReceived(CameraMetadataNative result,
853 CaptureResultExtras resultExtras) throws RemoteException {
854 int requestId = resultExtras.getRequestId();
Zhijun Heecb323e2013-07-31 09:40:27 -0700855 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700856 Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
857 + requestId);
Zhijun Heecb323e2013-07-31 09:40:27 -0700858 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700859 final CaptureListenerHolder holder;
860 synchronized (mLock) {
861 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
862 }
Igor Murashkin70725502013-06-25 20:27:06 +0000863
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800864 Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
865 boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
866
Jianing Weibaf0c652014-04-18 17:35:00 -0700867 // Update tracker (increment counter) when it's not a partial result.
868 if (!quirkIsPartialResult) {
869 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
870 }
871
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700872 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000873 if (holder == null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700874 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700875 Log.d(TAG,
876 "holder is null, early return at frame "
877 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700878 }
Igor Murashkin70725502013-06-25 20:27:06 +0000879 return;
880 }
881
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700882 if (isClosed()) {
883 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700884 Log.d(TAG,
885 "camera is closed, early return at frame "
886 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700887 }
888 return;
889 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700890
Jianing Weid2c3a822014-03-27 18:27:43 -0700891 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
Igor Murashkindb075af2014-05-21 10:07:08 -0700892
Igor Murashkin70725502013-06-25 20:27:06 +0000893
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800894 Runnable resultDispatch = null;
895
896 // Either send a partial result or the final capture completed result
897 if (quirkIsPartialResult) {
Igor Murashkindb075af2014-05-21 10:07:08 -0700898 final CaptureResult resultAsCapture =
899 new CaptureResult(result, request, requestId);
900
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800901 // Partial result
902 resultDispatch = new Runnable() {
903 @Override
904 public void run() {
905 if (!CameraDevice.this.isClosed()){
906 holder.getListener().onCapturePartial(
907 CameraDevice.this,
908 request,
909 resultAsCapture);
910 }
911 }
912 };
913 } else {
Igor Murashkindb075af2014-05-21 10:07:08 -0700914 final TotalCaptureResult resultAsCapture =
915 new TotalCaptureResult(result, request, requestId);
916
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800917 // Final capture result
918 resultDispatch = new Runnable() {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700919 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700920 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700921 if (!CameraDevice.this.isClosed()){
922 holder.getListener().onCaptureCompleted(
923 CameraDevice.this,
924 request,
925 resultAsCapture);
926 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700927 }
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800928 };
929 }
930
931 holder.getHandler().post(resultDispatch);
Jianing Weid2c3a822014-03-27 18:27:43 -0700932
933 // Fire onCaptureSequenceCompleted
934 if (!quirkIsPartialResult) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700935 checkAndFireSequenceComplete();
936 }
Igor Murashkin70725502013-06-25 20:27:06 +0000937 }
938
939 }
940
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700941 /**
942 * Default handler management. If handler is null, get the current thread's
943 * Looper to create a Handler with. If no looper exists, throw exception.
944 */
945 private Handler checkHandler(Handler handler) {
946 if (handler == null) {
947 Looper looper = Looper.myLooper();
948 if (looper == null) {
949 throw new IllegalArgumentException(
950 "No handler given, and current thread has no looper!");
951 }
952 handler = new Handler(looper);
953 }
954 return handler;
955 }
956
Zhijun He7f4d3142013-07-23 07:54:38 -0700957 private void checkIfCameraClosed() {
958 if (mRemoteDevice == null) {
959 throw new IllegalStateException("CameraDevice was already closed");
960 }
961 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700962
963 private boolean isClosed() {
964 synchronized(mLock) {
965 return (mRemoteDevice == null);
966 }
967 }
Igor Murashkin70725502013-06-25 20:27:06 +0000968}