blob: b082a70764527020c5ca4cff09bc33a0ff29b71d [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;
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -070027import android.hardware.camera2.utils.CameraBinderDecorator;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070028import android.hardware.camera2.utils.CameraRuntimeException;
Igor Murashkin9c595172014-05-12 13:56:20 -070029import android.hardware.camera2.utils.LongParcelable;
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;
Eino-Ville Talvalab72d1ef2014-04-28 13:21:18 -0700160
161 final int MAX_TAG_LEN = 23;
162 String tag = String.format("CameraDevice-JV-%s", mCameraId);
163 if (tag.length() > MAX_TAG_LEN) {
164 tag = tag.substring(0, MAX_TAG_LEN);
165 }
166 TAG = tag;
167
Zhijun Heecb323e2013-07-31 09:40:27 -0700168 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +0000169 }
170
171 public CameraDeviceCallbacks getCallbacks() {
172 return mCallbacks;
173 }
174
Igor Murashkin70725502013-06-25 20:27:06 +0000175 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -0700176 // TODO: Move from decorator to direct binder-mediated exceptions
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700177 synchronized(mLock) {
178 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
179
180 mDeviceHandler.post(mCallOnOpened);
181 mDeviceHandler.post(mCallOnUnconfigured);
182 }
Igor Murashkin70725502013-06-25 20:27:06 +0000183 }
184
185 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700186 public String getId() {
187 return mCameraId;
188 }
189
190 @Override
Igor Murashkin70725502013-06-25 20:27:06 +0000191 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700192 // Treat a null input the same an empty list
193 if (outputs == null) {
194 outputs = new ArrayList<Surface>();
195 }
Igor Murashkin70725502013-06-25 20:27:06 +0000196 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700197 checkIfCameraClosed();
198
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700199 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
200 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
201
202 // Determine which streams need to be created, which to be deleted
203 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
204 int streamId = mConfiguredOutputs.keyAt(i);
205 Surface s = mConfiguredOutputs.valueAt(i);
206
207 if (!outputs.contains(s)) {
208 deleteList.add(streamId);
209 } else {
210 addSet.remove(s); // Don't create a stream previously created
211 }
212 }
213
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700214 mDeviceHandler.post(mCallOnBusy);
215 stopRepeating();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700216
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700217 try {
Ruben Brunkdecfe952013-10-29 11:00:32 -0700218 waitUntilIdle();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700219
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700220 mRemoteDevice.beginConfigure();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700221 // Delete all streams first (to free up HW resources)
222 for (Integer streamId : deleteList) {
223 mRemoteDevice.deleteStream(streamId);
224 mConfiguredOutputs.delete(streamId);
225 }
226
227 // Add all new streams
228 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000229 // TODO: remove width,height,format since we are ignoring
230 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700231 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
232 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000233 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700234
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700235 mRemoteDevice.endConfigure();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700236 } catch (CameraRuntimeException e) {
237 if (e.getReason() == CAMERA_IN_USE) {
238 throw new IllegalStateException("The camera is currently busy." +
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700239 " You must wait until the previous operation completes.");
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700240 }
241
242 throw e.asChecked();
243 } catch (RemoteException e) {
244 // impossible
245 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000246 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700247
248 if (outputs.size() > 0) {
249 mDeviceHandler.post(mCallOnIdle);
250 } else {
251 mDeviceHandler.post(mCallOnUnconfigured);
252 }
Igor Murashkin70725502013-06-25 20:27:06 +0000253 }
254 }
255
256 @Override
Eino-Ville Talvalacca00c62014-05-14 10:53:20 -0700257 public void createCaptureSession(List<Surface> outputs,
258 CameraCaptureSession.StateListener listener, Handler handler)
259 throws CameraAccessException {
260 // TODO
261 }
262
263 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700264 public CaptureRequest.Builder createCaptureRequest(int templateType)
265 throws CameraAccessException {
Igor Murashkin70725502013-06-25 20:27:06 +0000266 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700267 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000268
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700269 CameraMetadataNative templatedRequest = new CameraMetadataNative();
Igor Murashkin70725502013-06-25 20:27:06 +0000270
271 try {
272 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
273 } catch (CameraRuntimeException e) {
274 throw e.asChecked();
275 } catch (RemoteException e) {
276 // impossible
277 return null;
278 }
279
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700280 CaptureRequest.Builder builder =
281 new CaptureRequest.Builder(templatedRequest);
Igor Murashkin70725502013-06-25 20:27:06 +0000282
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700283 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000284 }
285 }
286
287 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700288 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000289 throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700290 if (DEBUG) {
291 Log.d(TAG, "calling capture");
292 }
293 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
294 requestList.add(request);
295 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000296 }
297
298 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700299 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700300 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700301 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700302 if (requests.isEmpty()) {
303 Log.w(TAG, "Capture burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700304 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700305 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700306 return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000307 }
308
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700309 /**
310 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
311 * starting and stopping repeating request and flushing.
312 *
313 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
314 * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
315 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
316 * is added to the list mFrameNumberRequestPairs.</p>
317 *
318 * @param requestId the request ID of the current repeating request.
319 *
320 * @param lastFrameNumber last frame number returned from binder.
321 */
322 private void checkEarlyTriggerSequenceComplete(
323 final int requestId, final long lastFrameNumber) {
324 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
325 // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
326 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
327 final CaptureListenerHolder holder;
328 int index = mCaptureListenerMap.indexOfKey(requestId);
329 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
330 if (holder != null) {
331 mCaptureListenerMap.removeAt(index);
Jianing Weibaf0c652014-04-18 17:35:00 -0700332 if (DEBUG) {
333 Log.v(TAG, String.format(
334 "remove holder for requestId %d, "
335 + "because lastFrame is %d.",
336 requestId, lastFrameNumber));
337 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700338 }
339
340 if (holder != null) {
341 if (DEBUG) {
342 Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
343 + " request did not reach HAL");
344 }
345
346 Runnable resultDispatch = new Runnable() {
347 @Override
348 public void run() {
349 if (!CameraDevice.this.isClosed()) {
350 if (DEBUG) {
351 Log.d(TAG, String.format(
352 "early trigger sequence complete for request %d",
353 requestId));
354 }
355 if (lastFrameNumber < Integer.MIN_VALUE
356 || lastFrameNumber > Integer.MAX_VALUE) {
357 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
358 }
359 holder.getListener().onCaptureSequenceCompleted(
360 CameraDevice.this,
361 requestId,
362 (int)lastFrameNumber);
363 }
364 }
365 };
366 holder.getHandler().post(resultDispatch);
367 } else {
368 Log.w(TAG, String.format(
369 "did not register listener to request %d",
370 requestId));
371 }
372 } else {
373 mFrameNumberRequestPairs.add(
374 new SimpleEntry<Long, Integer>(lastFrameNumber,
375 requestId));
376 }
377 }
378
Jianing Weid2c3a822014-03-27 18:27:43 -0700379 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700380 Handler handler, boolean repeating) throws CameraAccessException {
381
382 // Need a valid handler, or current thread needs to have a looper, if
383 // listener is valid
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700384 if (listener != null) {
385 handler = checkHandler(handler);
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700386 }
Igor Murashkin70725502013-06-25 20:27:06 +0000387
388 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700389 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000390 int requestId;
391
Ruben Brunke73b41b2013-11-07 19:30:43 -0800392 if (repeating) {
393 stopRepeating();
394 }
395
Jianing Weid2c3a822014-03-27 18:27:43 -0700396 LongParcelable lastFrameNumberRef = new LongParcelable();
Igor Murashkin70725502013-06-25 20:27:06 +0000397 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700398 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
399 /*out*/lastFrameNumberRef);
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700400 if (DEBUG) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700401 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
402 }
Igor Murashkin70725502013-06-25 20:27:06 +0000403 } catch (CameraRuntimeException e) {
404 throw e.asChecked();
405 } catch (RemoteException e) {
406 // impossible
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700407 return -1;
Igor Murashkin70725502013-06-25 20:27:06 +0000408 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700409
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700410 if (listener != null) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700411 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
412 requestList, handler, repeating));
Jianing Weibaf0c652014-04-18 17:35:00 -0700413 } else {
414 if (DEBUG) {
415 Log.d(TAG, "Listen for request " + requestId + " is null");
416 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700417 }
Igor Murashkin70725502013-06-25 20:27:06 +0000418
Jianing Weid2c3a822014-03-27 18:27:43 -0700419 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700420
Igor Murashkin70725502013-06-25 20:27:06 +0000421 if (repeating) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700422 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700423 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700424 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700425 mRepeatingRequestId = requestId;
Jianing Weid2c3a822014-03-27 18:27:43 -0700426 } else {
427 mFrameNumberRequestPairs.add(
428 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
Igor Murashkin70725502013-06-25 20:27:06 +0000429 }
430
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700431 if (mIdle) {
432 mDeviceHandler.post(mCallOnActive);
433 }
434 mIdle = false;
435
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700436 return requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000437 }
438 }
439
440 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700441 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700442 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700443 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
444 requestList.add(request);
445 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000446 }
447
448 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700449 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700450 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700451 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700452 if (requests.isEmpty()) {
453 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700454 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700455 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700456 return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000457 }
458
459 @Override
460 public void stopRepeating() throws CameraAccessException {
461
462 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700463 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700464 if (mRepeatingRequestId != REQUEST_ID_NONE) {
465
466 int requestId = mRepeatingRequestId;
467 mRepeatingRequestId = REQUEST_ID_NONE;
468
469 // Queue for deletion after in-flight requests finish
Zhijun He1a9b6462014-03-31 16:11:33 -0700470 if (mCaptureListenerMap.get(requestId) != null) {
471 mRepeatingRequestIdDeletedList.add(requestId);
472 }
Igor Murashkin70725502013-06-25 20:27:06 +0000473
474 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700475 LongParcelable lastFrameNumberRef = new LongParcelable();
476 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
477 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700478
479 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
480
Igor Murashkin70725502013-06-25 20:27:06 +0000481 } catch (CameraRuntimeException e) {
482 throw e.asChecked();
483 } catch (RemoteException e) {
484 // impossible
485 return;
486 }
487 }
488 }
489 }
490
Zhijun Hed842fcd2013-12-26 14:14:04 -0800491 private void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700492
493 synchronized (mLock) {
494 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700495 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Zhijun He7f4d3142013-07-23 07:54:38 -0700496 throw new IllegalStateException("Active repeating request ongoing");
497 }
498
499 try {
500 mRemoteDevice.waitUntilIdle();
501 } catch (CameraRuntimeException e) {
502 throw e.asChecked();
503 } catch (RemoteException e) {
504 // impossible
505 return;
506 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700507
508 mRepeatingRequestId = REQUEST_ID_NONE;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700509 }
Igor Murashkin70725502013-06-25 20:27:06 +0000510 }
511
512 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700513 public void flush() throws CameraAccessException {
514 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700515 checkIfCameraClosed();
516
517 mDeviceHandler.post(mCallOnBusy);
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700518 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700519 LongParcelable lastFrameNumberRef = new LongParcelable();
520 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
521 if (mRepeatingRequestId != REQUEST_ID_NONE) {
522 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700523 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700524 mRepeatingRequestId = REQUEST_ID_NONE;
525 }
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700526 } catch (CameraRuntimeException e) {
527 throw e.asChecked();
528 } catch (RemoteException e) {
529 // impossible
530 return;
531 }
532 }
533 }
534
535 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700536 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000537 synchronized (mLock) {
538
539 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700540 if (mRemoteDevice != null) {
541 mRemoteDevice.disconnect();
542 }
Igor Murashkin70725502013-06-25 20:27:06 +0000543 } catch (CameraRuntimeException e) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700544 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000545 } catch (RemoteException e) {
546 // impossible
547 }
548
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700549 if (mRemoteDevice != null) {
550 mDeviceHandler.post(mCallOnClosed);
551 }
Igor Murashkin70725502013-06-25 20:27:06 +0000552
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700553 mRemoteDevice = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000554 }
555 }
556
557 @Override
558 protected void finalize() throws Throwable {
559 try {
560 close();
Igor Murashkin70725502013-06-25 20:27:06 +0000561 }
562 finally {
563 super.finalize();
564 }
565 }
566
567 static class CaptureListenerHolder {
568
569 private final boolean mRepeating;
570 private final CaptureListener mListener;
Jianing Weid2c3a822014-03-27 18:27:43 -0700571 private final List<CaptureRequest> mRequestList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700572 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000573
Jianing Weid2c3a822014-03-27 18:27:43 -0700574 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
575 Handler handler, boolean repeating) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700576 if (listener == null || handler == null) {
577 throw new UnsupportedOperationException(
578 "Must have a valid handler and a valid listener");
579 }
Igor Murashkin70725502013-06-25 20:27:06 +0000580 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700581 mHandler = handler;
Jianing Weid2c3a822014-03-27 18:27:43 -0700582 mRequestList = new ArrayList<CaptureRequest>(requestList);
Igor Murashkin70725502013-06-25 20:27:06 +0000583 mListener = listener;
584 }
585
586 public boolean isRepeating() {
587 return mRepeating;
588 }
589
590 public CaptureListener getListener() {
591 return mListener;
592 }
593
Jianing Weid2c3a822014-03-27 18:27:43 -0700594 public CaptureRequest getRequest(int subsequenceId) {
595 if (subsequenceId >= mRequestList.size()) {
596 throw new IllegalArgumentException(
597 String.format(
598 "Requested subsequenceId %d is larger than request list size %d.",
599 subsequenceId, mRequestList.size()));
600 } else {
601 if (subsequenceId < 0) {
602 throw new IllegalArgumentException(String.format(
603 "Requested subsequenceId %d is negative", subsequenceId));
604 } else {
605 return mRequestList.get(subsequenceId);
606 }
607 }
608 }
609
Igor Murashkin70725502013-06-25 20:27:06 +0000610 public CaptureRequest getRequest() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700611 return getRequest(0);
Igor Murashkin70725502013-06-25 20:27:06 +0000612 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700613
614 public Handler getHandler() {
615 return mHandler;
616 }
617
Igor Murashkin70725502013-06-25 20:27:06 +0000618 }
619
Jianing Weid2c3a822014-03-27 18:27:43 -0700620 /**
621 * This class tracks the last frame number for submitted requests.
622 */
623 public class FrameNumberTracker {
624
625 private long mCompletedFrameNumber = -1;
626 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
627
628 private void update() {
629 Iterator<Long> iter = mFutureErrorSet.iterator();
630 while (iter.hasNext()) {
631 long errorFrameNumber = iter.next();
632 if (errorFrameNumber == mCompletedFrameNumber + 1) {
633 mCompletedFrameNumber++;
634 iter.remove();
635 } else {
636 break;
637 }
638 }
639 }
640
641 /**
642 * This function is called every time when a result or an error is received.
643 * @param frameNumber: the frame number corresponding to the result or error
644 * @param isError: true if it is an error, false if it is not an error
645 */
646 public void updateTracker(long frameNumber, boolean isError) {
647 if (isError) {
648 mFutureErrorSet.add(frameNumber);
649 } else {
650 /**
651 * HAL cannot send an OnResultReceived for frame N unless it knows for
652 * sure that all frames prior to N have either errored out or completed.
653 * So if the current frame is not an error, then all previous frames
654 * should have arrived. The following line checks whether this holds.
655 */
656 if (frameNumber != mCompletedFrameNumber + 1) {
Zhijun He22589b42014-05-01 10:37:36 -0700657 Log.e(TAG, String.format(
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700658 "result frame number %d comes out of order, should be %d + 1",
659 frameNumber, mCompletedFrameNumber));
Jianing Weid2c3a822014-03-27 18:27:43 -0700660 }
661 mCompletedFrameNumber++;
662 }
663 update();
664 }
665
666 public long getCompletedFrameNumber() {
667 return mCompletedFrameNumber;
668 }
669
670 }
671
672 private void checkAndFireSequenceComplete() {
673 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
674 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
675 while (iter.hasNext()) {
676 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
677 if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
678
679 // remove request from mCaptureListenerMap
680 final int requestId = frameNumberRequestPair.getValue();
681 final CaptureListenerHolder holder;
682 synchronized (mLock) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700683 int index = mCaptureListenerMap.indexOfKey(requestId);
684 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
Jianing Weid2c3a822014-03-27 18:27:43 -0700685 : null;
686 if (holder != null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700687 mCaptureListenerMap.removeAt(index);
688 if (DEBUG) {
689 Log.v(TAG, String.format(
690 "remove holder for requestId %d, "
691 + "because lastFrame %d is <= %d",
692 requestId, frameNumberRequestPair.getKey(),
693 completedFrameNumber));
694 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700695 }
696 }
697 iter.remove();
698
699 // Call onCaptureSequenceCompleted
700 if (holder != null) {
701 Runnable resultDispatch = new Runnable() {
702 @Override
703 public void run() {
704 if (!CameraDevice.this.isClosed()){
705 if (DEBUG) {
706 Log.d(TAG, String.format(
707 "fire sequence complete for request %d",
708 requestId));
709 }
710
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700711 long lastFrameNumber = frameNumberRequestPair.getKey();
712 if (lastFrameNumber < Integer.MIN_VALUE
713 || lastFrameNumber > Integer.MAX_VALUE) {
714 throw new AssertionError(lastFrameNumber
715 + " cannot be cast to int");
716 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700717 holder.getListener().onCaptureSequenceCompleted(
718 CameraDevice.this,
719 requestId,
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700720 (int)lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700721 }
722 }
723 };
724 holder.getHandler().post(resultDispatch);
725 }
726
727 }
728 }
729 }
730
Zhijun Heecb323e2013-07-31 09:40:27 -0700731 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000732
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700733 //
734 // Constants below need to be kept up-to-date with
735 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
736 //
737
738 //
739 // Error codes for onCameraError
740 //
741
742 /**
743 * Camera has been disconnected
744 */
745 static final int ERROR_CAMERA_DISCONNECTED = 0;
746
747 /**
748 * Camera has encountered a device-level error
749 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
750 */
751 static final int ERROR_CAMERA_DEVICE = 1;
752
753 /**
754 * Camera has encountered a service-level error
755 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
756 */
757 static final int ERROR_CAMERA_SERVICE = 2;
758
Igor Murashkin70725502013-06-25 20:27:06 +0000759 @Override
760 public IBinder asBinder() {
761 return this;
762 }
763
Igor Murashkin70725502013-06-25 20:27:06 +0000764 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700765 public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700766 Runnable r = null;
767 if (isClosed()) return;
768
769 synchronized(mLock) {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700770 switch (errorCode) {
771 case ERROR_CAMERA_DISCONNECTED:
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700772 r = mCallOnDisconnected;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700773 break;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700774 default:
775 Log.e(TAG, "Unknown error from camera device: " + errorCode);
776 // no break
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700777 case ERROR_CAMERA_DEVICE:
778 case ERROR_CAMERA_SERVICE:
779 r = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700780 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700781 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700782 if (!CameraDevice.this.isClosed()) {
783 mDeviceListener.onError(CameraDevice.this, errorCode);
784 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700785 }
786 };
787 break;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700788 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700789 CameraDevice.this.mDeviceHandler.post(r);
Zhijun Heecb323e2013-07-31 09:40:27 -0700790 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700791
792 // Fire onCaptureSequenceCompleted
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700793 if (DEBUG) {
794 Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
795 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700796 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
797 checkAndFireSequenceComplete();
798
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700799 }
800
801 @Override
802 public void onCameraIdle() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700803 if (isClosed()) return;
804
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700805 if (DEBUG) {
806 Log.d(TAG, "Camera now idle");
807 }
808 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700809 if (!CameraDevice.this.mIdle) {
810 CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
811 }
812 CameraDevice.this.mIdle = true;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700813 }
814 }
815
816 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700817 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
818 int requestId = resultExtras.getRequestId();
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700819 if (DEBUG) {
820 Log.d(TAG, "Capture started for id " + requestId);
821 }
822 final CaptureListenerHolder holder;
823
824 // Get the listener for this frame ID, if there is one
825 synchronized (mLock) {
826 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
827 }
828
829 if (holder == null) {
830 return;
831 }
832
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700833 if (isClosed()) return;
834
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700835 // Dispatch capture start notice
836 holder.getHandler().post(
837 new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700838 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700839 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700840 if (!CameraDevice.this.isClosed()) {
841 holder.getListener().onCaptureStarted(
842 CameraDevice.this,
Jianing Weid2c3a822014-03-27 18:27:43 -0700843 holder.getRequest(resultExtras.getSubsequenceId()),
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700844 timestamp);
845 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700846 }
847 });
Igor Murashkin70725502013-06-25 20:27:06 +0000848 }
849
850 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700851 public void onResultReceived(CameraMetadataNative result,
852 CaptureResultExtras resultExtras) throws RemoteException {
853 int requestId = resultExtras.getRequestId();
Zhijun Heecb323e2013-07-31 09:40:27 -0700854 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700855 Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
856 + requestId);
Zhijun Heecb323e2013-07-31 09:40:27 -0700857 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700858 final CaptureListenerHolder holder;
859 synchronized (mLock) {
860 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
861 }
Igor Murashkin70725502013-06-25 20:27:06 +0000862
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800863 Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
864 boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
865
Jianing Weibaf0c652014-04-18 17:35:00 -0700866 // Update tracker (increment counter) when it's not a partial result.
867 if (!quirkIsPartialResult) {
868 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
869 }
870
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700871 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000872 if (holder == null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700873 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700874 Log.d(TAG,
875 "holder is null, early return at frame "
876 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700877 }
Igor Murashkin70725502013-06-25 20:27:06 +0000878 return;
879 }
880
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700881 if (isClosed()) {
882 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700883 Log.d(TAG,
884 "camera is closed, early return at frame "
885 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700886 }
887 return;
888 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700889
Jianing Weid2c3a822014-03-27 18:27:43 -0700890 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700891 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000892
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800893 Runnable resultDispatch = null;
894
895 // Either send a partial result or the final capture completed result
896 if (quirkIsPartialResult) {
897 // Partial result
898 resultDispatch = new Runnable() {
899 @Override
900 public void run() {
901 if (!CameraDevice.this.isClosed()){
902 holder.getListener().onCapturePartial(
903 CameraDevice.this,
904 request,
905 resultAsCapture);
906 }
907 }
908 };
909 } else {
910 // Final capture result
911 resultDispatch = new Runnable() {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700912 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700913 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700914 if (!CameraDevice.this.isClosed()){
915 holder.getListener().onCaptureCompleted(
916 CameraDevice.this,
917 request,
918 resultAsCapture);
919 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700920 }
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800921 };
922 }
923
924 holder.getHandler().post(resultDispatch);
Jianing Weid2c3a822014-03-27 18:27:43 -0700925
926 // Fire onCaptureSequenceCompleted
927 if (!quirkIsPartialResult) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700928 checkAndFireSequenceComplete();
929 }
Igor Murashkin70725502013-06-25 20:27:06 +0000930 }
931
932 }
933
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700934 /**
935 * Default handler management. If handler is null, get the current thread's
936 * Looper to create a Handler with. If no looper exists, throw exception.
937 */
938 private Handler checkHandler(Handler handler) {
939 if (handler == null) {
940 Looper looper = Looper.myLooper();
941 if (looper == null) {
942 throw new IllegalArgumentException(
943 "No handler given, and current thread has no looper!");
944 }
945 handler = new Handler(looper);
946 }
947 return handler;
948 }
949
Zhijun He7f4d3142013-07-23 07:54:38 -0700950 private void checkIfCameraClosed() {
951 if (mRemoteDevice == null) {
952 throw new IllegalStateException("CameraDevice was already closed");
953 }
954 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700955
956 private boolean isClosed() {
957 synchronized(mLock) {
958 return (mRemoteDevice == null);
959 }
960 }
Igor Murashkin70725502013-06-25 20:27:06 +0000961}