blob: e78ffff297c5b80b0088fd759f5cb6db659047f3 [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;
24import android.hardware.camera2.ICameraDeviceCallbacks;
25import android.hardware.camera2.ICameraDeviceUser;
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -070026import android.hardware.camera2.utils.CameraBinderDecorator;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070027import android.hardware.camera2.utils.CameraRuntimeException;
Igor Murashkin9c595172014-05-12 13:56:20 -070028import android.hardware.camera2.utils.LongParcelable;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070029import android.os.Handler;
Ruben Brunkdecfe952013-10-29 11:00:32 -070030import android.os.IBinder;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070031import android.os.Looper;
Ruben Brunkdecfe952013-10-29 11:00:32 -070032import android.os.RemoteException;
Igor Murashkin70725502013-06-25 20:27:06 +000033import android.util.Log;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070034import android.util.SparseArray;
Igor Murashkin70725502013-06-25 20:27:06 +000035import android.view.Surface;
36
Jianing Weid2c3a822014-03-27 18:27:43 -070037import java.util.AbstractMap.SimpleEntry;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070038import java.util.ArrayList;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070039import java.util.HashSet;
Ruben Brunkdecfe952013-10-29 11:00:32 -070040import java.util.Iterator;
Igor Murashkin70725502013-06-25 20:27:06 +000041import java.util.List;
Jianing Weid2c3a822014-03-27 18:27:43 -070042import java.util.TreeSet;
Igor Murashkin70725502013-06-25 20:27:06 +000043
44/**
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070045 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
Igor Murashkin70725502013-06-25 20:27:06 +000046 */
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070047public class CameraDevice implements android.hardware.camera2.CameraDevice {
Igor Murashkin70725502013-06-25 20:27:06 +000048
49 private final String TAG;
Zhijun Heecb323e2013-07-31 09:40:27 -070050 private final boolean DEBUG;
Igor Murashkin70725502013-06-25 20:27:06 +000051
Ruben Brunkdecfe952013-10-29 11:00:32 -070052 private static final int REQUEST_ID_NONE = -1;
53
Igor Murashkin70725502013-06-25 20:27:06 +000054 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
55 private ICameraDeviceUser mRemoteDevice;
56
57 private final Object mLock = new Object();
58 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
59
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070060 private final StateListener mDeviceListener;
61 private final Handler mDeviceHandler;
62
63 private boolean mIdle = true;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070064
65 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
66 new SparseArray<CaptureListenerHolder>();
Igor Murashkin70725502013-06-25 20:27:06 +000067
Ruben Brunkdecfe952013-10-29 11:00:32 -070068 private int mRepeatingRequestId = REQUEST_ID_NONE;
69 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
Igor Murashkin57ea59b2013-08-23 16:55:57 -070070 // Map stream IDs to Surfaces
71 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
Igor Murashkin70725502013-06-25 20:27:06 +000072
73 private final String mCameraId;
74
Jianing Weid2c3a822014-03-27 18:27:43 -070075 /**
76 * A list tracking request and its expected last frame.
77 * Updated when calling ICameraDeviceUser methods.
78 */
79 private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
80 mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
81
82 /**
83 * An object tracking received frame numbers.
84 * Updated when receiving callbacks from ICameraDeviceCallbacks.
85 */
86 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
87
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070088 // Runnables for all state transitions, except error, which needs the
89 // error code argument
90
91 private final Runnable mCallOnOpened = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -070092 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070093 public void run() {
94 if (!CameraDevice.this.isClosed()) {
95 mDeviceListener.onOpened(CameraDevice.this);
96 }
97 }
98 };
99
100 private final Runnable mCallOnUnconfigured = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700101 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700102 public void run() {
103 if (!CameraDevice.this.isClosed()) {
104 mDeviceListener.onUnconfigured(CameraDevice.this);
105 }
106 }
107 };
108
109 private final Runnable mCallOnActive = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700110 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700111 public void run() {
112 if (!CameraDevice.this.isClosed()) {
113 mDeviceListener.onActive(CameraDevice.this);
114 }
115 }
116 };
117
118 private final Runnable mCallOnBusy = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700119 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700120 public void run() {
121 if (!CameraDevice.this.isClosed()) {
122 mDeviceListener.onBusy(CameraDevice.this);
123 }
124 }
125 };
126
127 private final Runnable mCallOnClosed = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700128 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700129 public void run() {
Zhijun He2db56022014-01-02 15:12:00 -0800130 mDeviceListener.onClosed(CameraDevice.this);
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700131 }
132 };
133
134 private final Runnable mCallOnIdle = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700135 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700136 public void run() {
137 if (!CameraDevice.this.isClosed()) {
138 mDeviceListener.onIdle(CameraDevice.this);
139 }
140 }
141 };
142
143 private final Runnable mCallOnDisconnected = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700144 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700145 public void run() {
146 if (!CameraDevice.this.isClosed()) {
147 mDeviceListener.onDisconnected(CameraDevice.this);
148 }
149 }
150 };
151
152 public CameraDevice(String cameraId, StateListener listener, Handler handler) {
153 if (cameraId == null || listener == null || handler == null) {
154 throw new IllegalArgumentException("Null argument given");
155 }
Igor Murashkin70725502013-06-25 20:27:06 +0000156 mCameraId = cameraId;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700157 mDeviceListener = listener;
158 mDeviceHandler = handler;
Eino-Ville Talvalab72d1ef2014-04-28 13:21:18 -0700159
160 final int MAX_TAG_LEN = 23;
161 String tag = String.format("CameraDevice-JV-%s", mCameraId);
162 if (tag.length() > MAX_TAG_LEN) {
163 tag = tag.substring(0, MAX_TAG_LEN);
164 }
165 TAG = tag;
166
Zhijun Heecb323e2013-07-31 09:40:27 -0700167 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +0000168 }
169
170 public CameraDeviceCallbacks getCallbacks() {
171 return mCallbacks;
172 }
173
Igor Murashkin70725502013-06-25 20:27:06 +0000174 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -0700175 // TODO: Move from decorator to direct binder-mediated exceptions
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700176 synchronized(mLock) {
177 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
178
179 mDeviceHandler.post(mCallOnOpened);
180 mDeviceHandler.post(mCallOnUnconfigured);
181 }
Igor Murashkin70725502013-06-25 20:27:06 +0000182 }
183
184 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700185 public String getId() {
186 return mCameraId;
187 }
188
189 @Override
Igor Murashkin70725502013-06-25 20:27:06 +0000190 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700191 // Treat a null input the same an empty list
192 if (outputs == null) {
193 outputs = new ArrayList<Surface>();
194 }
Igor Murashkin70725502013-06-25 20:27:06 +0000195 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700196 checkIfCameraClosed();
197
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700198 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
199 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
200
201 // Determine which streams need to be created, which to be deleted
202 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
203 int streamId = mConfiguredOutputs.keyAt(i);
204 Surface s = mConfiguredOutputs.valueAt(i);
205
206 if (!outputs.contains(s)) {
207 deleteList.add(streamId);
208 } else {
209 addSet.remove(s); // Don't create a stream previously created
210 }
211 }
212
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700213 mDeviceHandler.post(mCallOnBusy);
214 stopRepeating();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700215
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700216 try {
Ruben Brunkdecfe952013-10-29 11:00:32 -0700217 waitUntilIdle();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700218
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700219 mRemoteDevice.beginConfigure();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700220 // Delete all streams first (to free up HW resources)
221 for (Integer streamId : deleteList) {
222 mRemoteDevice.deleteStream(streamId);
223 mConfiguredOutputs.delete(streamId);
224 }
225
226 // Add all new streams
227 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000228 // TODO: remove width,height,format since we are ignoring
229 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700230 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
231 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000232 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700233
Ruben Brunkfeb50af2014-05-09 19:58:49 -0700234 mRemoteDevice.endConfigure();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700235 } catch (CameraRuntimeException e) {
236 if (e.getReason() == CAMERA_IN_USE) {
237 throw new IllegalStateException("The camera is currently busy." +
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700238 " You must wait until the previous operation completes.");
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700239 }
240
241 throw e.asChecked();
242 } catch (RemoteException e) {
243 // impossible
244 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000245 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700246
247 if (outputs.size() > 0) {
248 mDeviceHandler.post(mCallOnIdle);
249 } else {
250 mDeviceHandler.post(mCallOnUnconfigured);
251 }
Igor Murashkin70725502013-06-25 20:27:06 +0000252 }
253 }
254
255 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700256 public CaptureRequest.Builder createCaptureRequest(int templateType)
257 throws CameraAccessException {
Igor Murashkin70725502013-06-25 20:27:06 +0000258 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700259 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000260
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700261 CameraMetadataNative templatedRequest = new CameraMetadataNative();
Igor Murashkin70725502013-06-25 20:27:06 +0000262
263 try {
264 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
265 } catch (CameraRuntimeException e) {
266 throw e.asChecked();
267 } catch (RemoteException e) {
268 // impossible
269 return null;
270 }
271
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700272 CaptureRequest.Builder builder =
273 new CaptureRequest.Builder(templatedRequest);
Igor Murashkin70725502013-06-25 20:27:06 +0000274
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700275 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000276 }
277 }
278
279 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700280 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000281 throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700282 if (DEBUG) {
283 Log.d(TAG, "calling capture");
284 }
285 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
286 requestList.add(request);
287 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000288 }
289
290 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700291 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700292 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700293 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700294 if (requests.isEmpty()) {
295 Log.w(TAG, "Capture burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700296 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700297 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700298 return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000299 }
300
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700301 /**
302 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
303 * starting and stopping repeating request and flushing.
304 *
305 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
306 * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
307 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
308 * is added to the list mFrameNumberRequestPairs.</p>
309 *
310 * @param requestId the request ID of the current repeating request.
311 *
312 * @param lastFrameNumber last frame number returned from binder.
313 */
314 private void checkEarlyTriggerSequenceComplete(
315 final int requestId, final long lastFrameNumber) {
316 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
317 // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
318 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
319 final CaptureListenerHolder holder;
320 int index = mCaptureListenerMap.indexOfKey(requestId);
321 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
322 if (holder != null) {
323 mCaptureListenerMap.removeAt(index);
Jianing Weibaf0c652014-04-18 17:35:00 -0700324 if (DEBUG) {
325 Log.v(TAG, String.format(
326 "remove holder for requestId %d, "
327 + "because lastFrame is %d.",
328 requestId, lastFrameNumber));
329 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700330 }
331
332 if (holder != null) {
333 if (DEBUG) {
334 Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
335 + " request did not reach HAL");
336 }
337
338 Runnable resultDispatch = new Runnable() {
339 @Override
340 public void run() {
341 if (!CameraDevice.this.isClosed()) {
342 if (DEBUG) {
343 Log.d(TAG, String.format(
344 "early trigger sequence complete for request %d",
345 requestId));
346 }
347 if (lastFrameNumber < Integer.MIN_VALUE
348 || lastFrameNumber > Integer.MAX_VALUE) {
349 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
350 }
351 holder.getListener().onCaptureSequenceCompleted(
352 CameraDevice.this,
353 requestId,
354 (int)lastFrameNumber);
355 }
356 }
357 };
358 holder.getHandler().post(resultDispatch);
359 } else {
360 Log.w(TAG, String.format(
361 "did not register listener to request %d",
362 requestId));
363 }
364 } else {
365 mFrameNumberRequestPairs.add(
366 new SimpleEntry<Long, Integer>(lastFrameNumber,
367 requestId));
368 }
369 }
370
Jianing Weid2c3a822014-03-27 18:27:43 -0700371 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700372 Handler handler, boolean repeating) throws CameraAccessException {
373
374 // Need a valid handler, or current thread needs to have a looper, if
375 // listener is valid
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700376 if (listener != null) {
377 handler = checkHandler(handler);
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700378 }
Igor Murashkin70725502013-06-25 20:27:06 +0000379
380 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700381 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000382 int requestId;
383
Ruben Brunke73b41b2013-11-07 19:30:43 -0800384 if (repeating) {
385 stopRepeating();
386 }
387
Jianing Weid2c3a822014-03-27 18:27:43 -0700388 LongParcelable lastFrameNumberRef = new LongParcelable();
Igor Murashkin70725502013-06-25 20:27:06 +0000389 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700390 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
391 /*out*/lastFrameNumberRef);
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700392 if (DEBUG) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700393 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
394 }
Igor Murashkin70725502013-06-25 20:27:06 +0000395 } catch (CameraRuntimeException e) {
396 throw e.asChecked();
397 } catch (RemoteException e) {
398 // impossible
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700399 return -1;
Igor Murashkin70725502013-06-25 20:27:06 +0000400 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700401
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700402 if (listener != null) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700403 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
404 requestList, handler, repeating));
Jianing Weibaf0c652014-04-18 17:35:00 -0700405 } else {
406 if (DEBUG) {
407 Log.d(TAG, "Listen for request " + requestId + " is null");
408 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700409 }
Igor Murashkin70725502013-06-25 20:27:06 +0000410
Jianing Weid2c3a822014-03-27 18:27:43 -0700411 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700412
Igor Murashkin70725502013-06-25 20:27:06 +0000413 if (repeating) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700414 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700415 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700416 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700417 mRepeatingRequestId = requestId;
Jianing Weid2c3a822014-03-27 18:27:43 -0700418 } else {
419 mFrameNumberRequestPairs.add(
420 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
Igor Murashkin70725502013-06-25 20:27:06 +0000421 }
422
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700423 if (mIdle) {
424 mDeviceHandler.post(mCallOnActive);
425 }
426 mIdle = false;
427
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700428 return requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000429 }
430 }
431
432 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700433 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700434 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700435 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
436 requestList.add(request);
437 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000438 }
439
440 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700441 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700442 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700443 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700444 if (requests.isEmpty()) {
445 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700446 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700447 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700448 return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000449 }
450
451 @Override
452 public void stopRepeating() throws CameraAccessException {
453
454 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700455 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700456 if (mRepeatingRequestId != REQUEST_ID_NONE) {
457
458 int requestId = mRepeatingRequestId;
459 mRepeatingRequestId = REQUEST_ID_NONE;
460
461 // Queue for deletion after in-flight requests finish
Zhijun He1a9b6462014-03-31 16:11:33 -0700462 if (mCaptureListenerMap.get(requestId) != null) {
463 mRepeatingRequestIdDeletedList.add(requestId);
464 }
Igor Murashkin70725502013-06-25 20:27:06 +0000465
466 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700467 LongParcelable lastFrameNumberRef = new LongParcelable();
468 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
469 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700470
471 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
472
Igor Murashkin70725502013-06-25 20:27:06 +0000473 } catch (CameraRuntimeException e) {
474 throw e.asChecked();
475 } catch (RemoteException e) {
476 // impossible
477 return;
478 }
479 }
480 }
481 }
482
Zhijun Hed842fcd2013-12-26 14:14:04 -0800483 private void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700484
485 synchronized (mLock) {
486 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700487 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Zhijun He7f4d3142013-07-23 07:54:38 -0700488 throw new IllegalStateException("Active repeating request ongoing");
489 }
490
491 try {
492 mRemoteDevice.waitUntilIdle();
493 } catch (CameraRuntimeException e) {
494 throw e.asChecked();
495 } catch (RemoteException e) {
496 // impossible
497 return;
498 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700499
500 mRepeatingRequestId = REQUEST_ID_NONE;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700501 }
Igor Murashkin70725502013-06-25 20:27:06 +0000502 }
503
504 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700505 public void flush() throws CameraAccessException {
506 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700507 checkIfCameraClosed();
508
509 mDeviceHandler.post(mCallOnBusy);
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700510 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700511 LongParcelable lastFrameNumberRef = new LongParcelable();
512 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
513 if (mRepeatingRequestId != REQUEST_ID_NONE) {
514 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700515 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700516 mRepeatingRequestId = REQUEST_ID_NONE;
517 }
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700518 } catch (CameraRuntimeException e) {
519 throw e.asChecked();
520 } catch (RemoteException e) {
521 // impossible
522 return;
523 }
524 }
525 }
526
527 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700528 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000529 synchronized (mLock) {
530
531 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700532 if (mRemoteDevice != null) {
533 mRemoteDevice.disconnect();
534 }
Igor Murashkin70725502013-06-25 20:27:06 +0000535 } catch (CameraRuntimeException e) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700536 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000537 } catch (RemoteException e) {
538 // impossible
539 }
540
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700541 if (mRemoteDevice != null) {
542 mDeviceHandler.post(mCallOnClosed);
543 }
Igor Murashkin70725502013-06-25 20:27:06 +0000544
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700545 mRemoteDevice = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000546 }
547 }
548
549 @Override
550 protected void finalize() throws Throwable {
551 try {
552 close();
Igor Murashkin70725502013-06-25 20:27:06 +0000553 }
554 finally {
555 super.finalize();
556 }
557 }
558
559 static class CaptureListenerHolder {
560
561 private final boolean mRepeating;
562 private final CaptureListener mListener;
Jianing Weid2c3a822014-03-27 18:27:43 -0700563 private final List<CaptureRequest> mRequestList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700564 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000565
Jianing Weid2c3a822014-03-27 18:27:43 -0700566 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
567 Handler handler, boolean repeating) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700568 if (listener == null || handler == null) {
569 throw new UnsupportedOperationException(
570 "Must have a valid handler and a valid listener");
571 }
Igor Murashkin70725502013-06-25 20:27:06 +0000572 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700573 mHandler = handler;
Jianing Weid2c3a822014-03-27 18:27:43 -0700574 mRequestList = new ArrayList<CaptureRequest>(requestList);
Igor Murashkin70725502013-06-25 20:27:06 +0000575 mListener = listener;
576 }
577
578 public boolean isRepeating() {
579 return mRepeating;
580 }
581
582 public CaptureListener getListener() {
583 return mListener;
584 }
585
Jianing Weid2c3a822014-03-27 18:27:43 -0700586 public CaptureRequest getRequest(int subsequenceId) {
587 if (subsequenceId >= mRequestList.size()) {
588 throw new IllegalArgumentException(
589 String.format(
590 "Requested subsequenceId %d is larger than request list size %d.",
591 subsequenceId, mRequestList.size()));
592 } else {
593 if (subsequenceId < 0) {
594 throw new IllegalArgumentException(String.format(
595 "Requested subsequenceId %d is negative", subsequenceId));
596 } else {
597 return mRequestList.get(subsequenceId);
598 }
599 }
600 }
601
Igor Murashkin70725502013-06-25 20:27:06 +0000602 public CaptureRequest getRequest() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700603 return getRequest(0);
Igor Murashkin70725502013-06-25 20:27:06 +0000604 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700605
606 public Handler getHandler() {
607 return mHandler;
608 }
609
Igor Murashkin70725502013-06-25 20:27:06 +0000610 }
611
Jianing Weid2c3a822014-03-27 18:27:43 -0700612 /**
613 * This class tracks the last frame number for submitted requests.
614 */
615 public class FrameNumberTracker {
616
617 private long mCompletedFrameNumber = -1;
618 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
619
620 private void update() {
621 Iterator<Long> iter = mFutureErrorSet.iterator();
622 while (iter.hasNext()) {
623 long errorFrameNumber = iter.next();
624 if (errorFrameNumber == mCompletedFrameNumber + 1) {
625 mCompletedFrameNumber++;
626 iter.remove();
627 } else {
628 break;
629 }
630 }
631 }
632
633 /**
634 * This function is called every time when a result or an error is received.
635 * @param frameNumber: the frame number corresponding to the result or error
636 * @param isError: true if it is an error, false if it is not an error
637 */
638 public void updateTracker(long frameNumber, boolean isError) {
639 if (isError) {
640 mFutureErrorSet.add(frameNumber);
641 } else {
642 /**
643 * HAL cannot send an OnResultReceived for frame N unless it knows for
644 * sure that all frames prior to N have either errored out or completed.
645 * So if the current frame is not an error, then all previous frames
646 * should have arrived. The following line checks whether this holds.
647 */
648 if (frameNumber != mCompletedFrameNumber + 1) {
Zhijun He22589b42014-05-01 10:37:36 -0700649 Log.e(TAG, String.format(
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700650 "result frame number %d comes out of order, should be %d + 1",
651 frameNumber, mCompletedFrameNumber));
Jianing Weid2c3a822014-03-27 18:27:43 -0700652 }
653 mCompletedFrameNumber++;
654 }
655 update();
656 }
657
658 public long getCompletedFrameNumber() {
659 return mCompletedFrameNumber;
660 }
661
662 }
663
664 private void checkAndFireSequenceComplete() {
665 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
666 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
667 while (iter.hasNext()) {
668 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
669 if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
670
671 // remove request from mCaptureListenerMap
672 final int requestId = frameNumberRequestPair.getValue();
673 final CaptureListenerHolder holder;
674 synchronized (mLock) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700675 int index = mCaptureListenerMap.indexOfKey(requestId);
676 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
Jianing Weid2c3a822014-03-27 18:27:43 -0700677 : null;
678 if (holder != null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700679 mCaptureListenerMap.removeAt(index);
680 if (DEBUG) {
681 Log.v(TAG, String.format(
682 "remove holder for requestId %d, "
683 + "because lastFrame %d is <= %d",
684 requestId, frameNumberRequestPair.getKey(),
685 completedFrameNumber));
686 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700687 }
688 }
689 iter.remove();
690
691 // Call onCaptureSequenceCompleted
692 if (holder != null) {
693 Runnable resultDispatch = new Runnable() {
694 @Override
695 public void run() {
696 if (!CameraDevice.this.isClosed()){
697 if (DEBUG) {
698 Log.d(TAG, String.format(
699 "fire sequence complete for request %d",
700 requestId));
701 }
702
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700703 long lastFrameNumber = frameNumberRequestPair.getKey();
704 if (lastFrameNumber < Integer.MIN_VALUE
705 || lastFrameNumber > Integer.MAX_VALUE) {
706 throw new AssertionError(lastFrameNumber
707 + " cannot be cast to int");
708 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700709 holder.getListener().onCaptureSequenceCompleted(
710 CameraDevice.this,
711 requestId,
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700712 (int)lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700713 }
714 }
715 };
716 holder.getHandler().post(resultDispatch);
717 }
718
719 }
720 }
721 }
722
Zhijun Heecb323e2013-07-31 09:40:27 -0700723 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000724
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700725 //
726 // Constants below need to be kept up-to-date with
727 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
728 //
729
730 //
731 // Error codes for onCameraError
732 //
733
734 /**
735 * Camera has been disconnected
736 */
737 static final int ERROR_CAMERA_DISCONNECTED = 0;
738
739 /**
740 * Camera has encountered a device-level error
741 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
742 */
743 static final int ERROR_CAMERA_DEVICE = 1;
744
745 /**
746 * Camera has encountered a service-level error
747 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
748 */
749 static final int ERROR_CAMERA_SERVICE = 2;
750
Igor Murashkin70725502013-06-25 20:27:06 +0000751 @Override
752 public IBinder asBinder() {
753 return this;
754 }
755
Igor Murashkin70725502013-06-25 20:27:06 +0000756 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700757 public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700758 Runnable r = null;
759 if (isClosed()) return;
760
761 synchronized(mLock) {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700762 switch (errorCode) {
763 case ERROR_CAMERA_DISCONNECTED:
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700764 r = mCallOnDisconnected;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700765 break;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700766 default:
767 Log.e(TAG, "Unknown error from camera device: " + errorCode);
768 // no break
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700769 case ERROR_CAMERA_DEVICE:
770 case ERROR_CAMERA_SERVICE:
771 r = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700772 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700773 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700774 if (!CameraDevice.this.isClosed()) {
775 mDeviceListener.onError(CameraDevice.this, errorCode);
776 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700777 }
778 };
779 break;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700780 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700781 CameraDevice.this.mDeviceHandler.post(r);
Zhijun Heecb323e2013-07-31 09:40:27 -0700782 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700783
784 // Fire onCaptureSequenceCompleted
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700785 if (DEBUG) {
786 Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
787 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700788 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
789 checkAndFireSequenceComplete();
790
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700791 }
792
793 @Override
794 public void onCameraIdle() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700795 if (isClosed()) return;
796
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700797 if (DEBUG) {
798 Log.d(TAG, "Camera now idle");
799 }
800 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700801 if (!CameraDevice.this.mIdle) {
802 CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
803 }
804 CameraDevice.this.mIdle = true;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700805 }
806 }
807
808 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700809 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
810 int requestId = resultExtras.getRequestId();
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700811 if (DEBUG) {
812 Log.d(TAG, "Capture started for id " + requestId);
813 }
814 final CaptureListenerHolder holder;
815
816 // Get the listener for this frame ID, if there is one
817 synchronized (mLock) {
818 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
819 }
820
821 if (holder == null) {
822 return;
823 }
824
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700825 if (isClosed()) return;
826
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700827 // Dispatch capture start notice
828 holder.getHandler().post(
829 new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700830 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700831 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700832 if (!CameraDevice.this.isClosed()) {
833 holder.getListener().onCaptureStarted(
834 CameraDevice.this,
Jianing Weid2c3a822014-03-27 18:27:43 -0700835 holder.getRequest(resultExtras.getSubsequenceId()),
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700836 timestamp);
837 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700838 }
839 });
Igor Murashkin70725502013-06-25 20:27:06 +0000840 }
841
842 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700843 public void onResultReceived(CameraMetadataNative result,
844 CaptureResultExtras resultExtras) throws RemoteException {
845 int requestId = resultExtras.getRequestId();
Zhijun Heecb323e2013-07-31 09:40:27 -0700846 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700847 Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
848 + requestId);
Zhijun Heecb323e2013-07-31 09:40:27 -0700849 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700850 final CaptureListenerHolder holder;
851 synchronized (mLock) {
852 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
853 }
Igor Murashkin70725502013-06-25 20:27:06 +0000854
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800855 Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
856 boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
857
Jianing Weibaf0c652014-04-18 17:35:00 -0700858 // Update tracker (increment counter) when it's not a partial result.
859 if (!quirkIsPartialResult) {
860 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
861 }
862
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700863 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000864 if (holder == null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700865 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700866 Log.d(TAG,
867 "holder is null, early return at frame "
868 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700869 }
Igor Murashkin70725502013-06-25 20:27:06 +0000870 return;
871 }
872
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700873 if (isClosed()) {
874 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700875 Log.d(TAG,
876 "camera is closed, early return at frame "
877 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700878 }
879 return;
880 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700881
Jianing Weid2c3a822014-03-27 18:27:43 -0700882 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700883 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000884
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800885 Runnable resultDispatch = null;
886
887 // Either send a partial result or the final capture completed result
888 if (quirkIsPartialResult) {
889 // Partial result
890 resultDispatch = new Runnable() {
891 @Override
892 public void run() {
893 if (!CameraDevice.this.isClosed()){
894 holder.getListener().onCapturePartial(
895 CameraDevice.this,
896 request,
897 resultAsCapture);
898 }
899 }
900 };
901 } else {
902 // Final capture result
903 resultDispatch = new Runnable() {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700904 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700905 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700906 if (!CameraDevice.this.isClosed()){
907 holder.getListener().onCaptureCompleted(
908 CameraDevice.this,
909 request,
910 resultAsCapture);
911 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700912 }
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800913 };
914 }
915
916 holder.getHandler().post(resultDispatch);
Jianing Weid2c3a822014-03-27 18:27:43 -0700917
918 // Fire onCaptureSequenceCompleted
919 if (!quirkIsPartialResult) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700920 checkAndFireSequenceComplete();
921 }
Igor Murashkin70725502013-06-25 20:27:06 +0000922 }
923
924 }
925
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700926 /**
927 * Default handler management. If handler is null, get the current thread's
928 * Looper to create a Handler with. If no looper exists, throw exception.
929 */
930 private Handler checkHandler(Handler handler) {
931 if (handler == null) {
932 Looper looper = Looper.myLooper();
933 if (looper == null) {
934 throw new IllegalArgumentException(
935 "No handler given, and current thread has no looper!");
936 }
937 handler = new Handler(looper);
938 }
939 return handler;
940 }
941
Zhijun He7f4d3142013-07-23 07:54:38 -0700942 private void checkIfCameraClosed() {
943 if (mRemoteDevice == null) {
944 throw new IllegalStateException("CameraDevice was already closed");
945 }
946 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700947
948 private boolean isClosed() {
949 synchronized(mLock) {
950 return (mRemoteDevice == null);
951 }
952 }
Igor Murashkin70725502013-06-25 20:27:06 +0000953}