blob: 628d1c319ac4adbd1a8efcbd7b4c5f59eb48d449 [file] [log] [blame]
Igor Murashkin70725502013-06-25 20:27:06 +00001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070017package android.hardware.camera2.impl;
Igor Murashkin70725502013-06-25 20:27:06 +000018
Igor Murashkin57ea59b2013-08-23 16:55:57 -070019import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
20
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070021import android.hardware.camera2.CameraAccessException;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070022import android.hardware.camera2.CaptureRequest;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070023import android.hardware.camera2.CaptureResult;
Jianing Weid2c3a822014-03-27 18:27:43 -070024import android.hardware.camera2.CaptureResultExtras;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070025import android.hardware.camera2.ICameraDeviceCallbacks;
26import android.hardware.camera2.ICameraDeviceUser;
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
220 // TODO: 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
235 // TODO: mRemoteDevice.endConfigure
236 } 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 Talvala70c22072013-08-27 12:09:04 -0700257 public CaptureRequest.Builder createCaptureRequest(int templateType)
258 throws CameraAccessException {
Igor Murashkin70725502013-06-25 20:27:06 +0000259 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700260 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000261
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700262 CameraMetadataNative templatedRequest = new CameraMetadataNative();
Igor Murashkin70725502013-06-25 20:27:06 +0000263
264 try {
265 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
266 } catch (CameraRuntimeException e) {
267 throw e.asChecked();
268 } catch (RemoteException e) {
269 // impossible
270 return null;
271 }
272
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700273 CaptureRequest.Builder builder =
274 new CaptureRequest.Builder(templatedRequest);
Igor Murashkin70725502013-06-25 20:27:06 +0000275
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700276 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000277 }
278 }
279
280 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700281 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000282 throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700283 if (DEBUG) {
284 Log.d(TAG, "calling capture");
285 }
286 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
287 requestList.add(request);
288 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000289 }
290
291 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700292 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700293 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700294 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700295 if (requests.isEmpty()) {
296 Log.w(TAG, "Capture burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700297 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700298 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700299 return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000300 }
301
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700302 /**
303 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
304 * starting and stopping repeating request and flushing.
305 *
306 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
307 * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
308 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
309 * is added to the list mFrameNumberRequestPairs.</p>
310 *
311 * @param requestId the request ID of the current repeating request.
312 *
313 * @param lastFrameNumber last frame number returned from binder.
314 */
315 private void checkEarlyTriggerSequenceComplete(
316 final int requestId, final long lastFrameNumber) {
317 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
318 // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
319 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
320 final CaptureListenerHolder holder;
321 int index = mCaptureListenerMap.indexOfKey(requestId);
322 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
323 if (holder != null) {
324 mCaptureListenerMap.removeAt(index);
Jianing Weibaf0c652014-04-18 17:35:00 -0700325 if (DEBUG) {
326 Log.v(TAG, String.format(
327 "remove holder for requestId %d, "
328 + "because lastFrame is %d.",
329 requestId, lastFrameNumber));
330 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700331 }
332
333 if (holder != null) {
334 if (DEBUG) {
335 Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
336 + " request did not reach HAL");
337 }
338
339 Runnable resultDispatch = new Runnable() {
340 @Override
341 public void run() {
342 if (!CameraDevice.this.isClosed()) {
343 if (DEBUG) {
344 Log.d(TAG, String.format(
345 "early trigger sequence complete for request %d",
346 requestId));
347 }
348 if (lastFrameNumber < Integer.MIN_VALUE
349 || lastFrameNumber > Integer.MAX_VALUE) {
350 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
351 }
352 holder.getListener().onCaptureSequenceCompleted(
353 CameraDevice.this,
354 requestId,
355 (int)lastFrameNumber);
356 }
357 }
358 };
359 holder.getHandler().post(resultDispatch);
360 } else {
361 Log.w(TAG, String.format(
362 "did not register listener to request %d",
363 requestId));
364 }
365 } else {
366 mFrameNumberRequestPairs.add(
367 new SimpleEntry<Long, Integer>(lastFrameNumber,
368 requestId));
369 }
370 }
371
Jianing Weid2c3a822014-03-27 18:27:43 -0700372 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700373 Handler handler, boolean repeating) throws CameraAccessException {
374
375 // Need a valid handler, or current thread needs to have a looper, if
376 // listener is valid
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700377 if (listener != null) {
378 handler = checkHandler(handler);
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700379 }
Igor Murashkin70725502013-06-25 20:27:06 +0000380
381 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700382 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000383 int requestId;
384
Ruben Brunke73b41b2013-11-07 19:30:43 -0800385 if (repeating) {
386 stopRepeating();
387 }
388
Jianing Weid2c3a822014-03-27 18:27:43 -0700389 LongParcelable lastFrameNumberRef = new LongParcelable();
Igor Murashkin70725502013-06-25 20:27:06 +0000390 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700391 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
392 /*out*/lastFrameNumberRef);
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700393 if (DEBUG) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700394 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
395 }
Igor Murashkin70725502013-06-25 20:27:06 +0000396 } catch (CameraRuntimeException e) {
397 throw e.asChecked();
398 } catch (RemoteException e) {
399 // impossible
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700400 return -1;
Igor Murashkin70725502013-06-25 20:27:06 +0000401 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700402
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700403 if (listener != null) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700404 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
405 requestList, handler, repeating));
Jianing Weibaf0c652014-04-18 17:35:00 -0700406 } else {
407 if (DEBUG) {
408 Log.d(TAG, "Listen for request " + requestId + " is null");
409 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700410 }
Igor Murashkin70725502013-06-25 20:27:06 +0000411
Jianing Weid2c3a822014-03-27 18:27:43 -0700412 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700413
Igor Murashkin70725502013-06-25 20:27:06 +0000414 if (repeating) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700415 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700416 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700417 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700418 mRepeatingRequestId = requestId;
Jianing Weid2c3a822014-03-27 18:27:43 -0700419 } else {
420 mFrameNumberRequestPairs.add(
421 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
Igor Murashkin70725502013-06-25 20:27:06 +0000422 }
423
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700424 if (mIdle) {
425 mDeviceHandler.post(mCallOnActive);
426 }
427 mIdle = false;
428
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700429 return requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000430 }
431 }
432
433 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700434 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700435 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700436 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
437 requestList.add(request);
438 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000439 }
440
441 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700442 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700443 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700444 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700445 if (requests.isEmpty()) {
446 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700447 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700448 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700449 return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000450 }
451
452 @Override
453 public void stopRepeating() throws CameraAccessException {
454
455 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700456 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700457 if (mRepeatingRequestId != REQUEST_ID_NONE) {
458
459 int requestId = mRepeatingRequestId;
460 mRepeatingRequestId = REQUEST_ID_NONE;
461
462 // Queue for deletion after in-flight requests finish
Zhijun He1a9b6462014-03-31 16:11:33 -0700463 if (mCaptureListenerMap.get(requestId) != null) {
464 mRepeatingRequestIdDeletedList.add(requestId);
465 }
Igor Murashkin70725502013-06-25 20:27:06 +0000466
467 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700468 LongParcelable lastFrameNumberRef = new LongParcelable();
469 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
470 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700471
472 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
473
Igor Murashkin70725502013-06-25 20:27:06 +0000474 } catch (CameraRuntimeException e) {
475 throw e.asChecked();
476 } catch (RemoteException e) {
477 // impossible
478 return;
479 }
480 }
481 }
482 }
483
Zhijun Hed842fcd2013-12-26 14:14:04 -0800484 private void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700485
486 synchronized (mLock) {
487 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700488 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Zhijun He7f4d3142013-07-23 07:54:38 -0700489 throw new IllegalStateException("Active repeating request ongoing");
490 }
491
492 try {
493 mRemoteDevice.waitUntilIdle();
494 } catch (CameraRuntimeException e) {
495 throw e.asChecked();
496 } catch (RemoteException e) {
497 // impossible
498 return;
499 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700500
501 mRepeatingRequestId = REQUEST_ID_NONE;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700502 }
Igor Murashkin70725502013-06-25 20:27:06 +0000503 }
504
505 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700506 public void flush() throws CameraAccessException {
507 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700508 checkIfCameraClosed();
509
510 mDeviceHandler.post(mCallOnBusy);
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700511 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700512 LongParcelable lastFrameNumberRef = new LongParcelable();
513 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
514 if (mRepeatingRequestId != REQUEST_ID_NONE) {
515 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700516 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700517 mRepeatingRequestId = REQUEST_ID_NONE;
518 }
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700519 } catch (CameraRuntimeException e) {
520 throw e.asChecked();
521 } catch (RemoteException e) {
522 // impossible
523 return;
524 }
525 }
526 }
527
528 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700529 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000530 synchronized (mLock) {
531
532 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700533 if (mRemoteDevice != null) {
534 mRemoteDevice.disconnect();
535 }
Igor Murashkin70725502013-06-25 20:27:06 +0000536 } catch (CameraRuntimeException e) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700537 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000538 } catch (RemoteException e) {
539 // impossible
540 }
541
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700542 if (mRemoteDevice != null) {
543 mDeviceHandler.post(mCallOnClosed);
544 }
Igor Murashkin70725502013-06-25 20:27:06 +0000545
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700546 mRemoteDevice = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000547 }
548 }
549
550 @Override
551 protected void finalize() throws Throwable {
552 try {
553 close();
Igor Murashkin70725502013-06-25 20:27:06 +0000554 }
555 finally {
556 super.finalize();
557 }
558 }
559
560 static class CaptureListenerHolder {
561
562 private final boolean mRepeating;
563 private final CaptureListener mListener;
Jianing Weid2c3a822014-03-27 18:27:43 -0700564 private final List<CaptureRequest> mRequestList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700565 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000566
Jianing Weid2c3a822014-03-27 18:27:43 -0700567 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
568 Handler handler, boolean repeating) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700569 if (listener == null || handler == null) {
570 throw new UnsupportedOperationException(
571 "Must have a valid handler and a valid listener");
572 }
Igor Murashkin70725502013-06-25 20:27:06 +0000573 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700574 mHandler = handler;
Jianing Weid2c3a822014-03-27 18:27:43 -0700575 mRequestList = new ArrayList<CaptureRequest>(requestList);
Igor Murashkin70725502013-06-25 20:27:06 +0000576 mListener = listener;
577 }
578
579 public boolean isRepeating() {
580 return mRepeating;
581 }
582
583 public CaptureListener getListener() {
584 return mListener;
585 }
586
Jianing Weid2c3a822014-03-27 18:27:43 -0700587 public CaptureRequest getRequest(int subsequenceId) {
588 if (subsequenceId >= mRequestList.size()) {
589 throw new IllegalArgumentException(
590 String.format(
591 "Requested subsequenceId %d is larger than request list size %d.",
592 subsequenceId, mRequestList.size()));
593 } else {
594 if (subsequenceId < 0) {
595 throw new IllegalArgumentException(String.format(
596 "Requested subsequenceId %d is negative", subsequenceId));
597 } else {
598 return mRequestList.get(subsequenceId);
599 }
600 }
601 }
602
Igor Murashkin70725502013-06-25 20:27:06 +0000603 public CaptureRequest getRequest() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700604 return getRequest(0);
Igor Murashkin70725502013-06-25 20:27:06 +0000605 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700606
607 public Handler getHandler() {
608 return mHandler;
609 }
610
Igor Murashkin70725502013-06-25 20:27:06 +0000611 }
612
Jianing Weid2c3a822014-03-27 18:27:43 -0700613 /**
614 * This class tracks the last frame number for submitted requests.
615 */
616 public class FrameNumberTracker {
617
618 private long mCompletedFrameNumber = -1;
619 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
620
621 private void update() {
622 Iterator<Long> iter = mFutureErrorSet.iterator();
623 while (iter.hasNext()) {
624 long errorFrameNumber = iter.next();
625 if (errorFrameNumber == mCompletedFrameNumber + 1) {
626 mCompletedFrameNumber++;
627 iter.remove();
628 } else {
629 break;
630 }
631 }
632 }
633
634 /**
635 * This function is called every time when a result or an error is received.
636 * @param frameNumber: the frame number corresponding to the result or error
637 * @param isError: true if it is an error, false if it is not an error
638 */
639 public void updateTracker(long frameNumber, boolean isError) {
640 if (isError) {
641 mFutureErrorSet.add(frameNumber);
642 } else {
643 /**
644 * HAL cannot send an OnResultReceived for frame N unless it knows for
645 * sure that all frames prior to N have either errored out or completed.
646 * So if the current frame is not an error, then all previous frames
647 * should have arrived. The following line checks whether this holds.
648 */
649 if (frameNumber != mCompletedFrameNumber + 1) {
Zhijun He22589b42014-05-01 10:37:36 -0700650 Log.e(TAG, String.format(
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700651 "result frame number %d comes out of order, should be %d + 1",
652 frameNumber, mCompletedFrameNumber));
Jianing Weid2c3a822014-03-27 18:27:43 -0700653 }
654 mCompletedFrameNumber++;
655 }
656 update();
657 }
658
659 public long getCompletedFrameNumber() {
660 return mCompletedFrameNumber;
661 }
662
663 }
664
665 private void checkAndFireSequenceComplete() {
666 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
667 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
668 while (iter.hasNext()) {
669 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
670 if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
671
672 // remove request from mCaptureListenerMap
673 final int requestId = frameNumberRequestPair.getValue();
674 final CaptureListenerHolder holder;
675 synchronized (mLock) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700676 int index = mCaptureListenerMap.indexOfKey(requestId);
677 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
Jianing Weid2c3a822014-03-27 18:27:43 -0700678 : null;
679 if (holder != null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700680 mCaptureListenerMap.removeAt(index);
681 if (DEBUG) {
682 Log.v(TAG, String.format(
683 "remove holder for requestId %d, "
684 + "because lastFrame %d is <= %d",
685 requestId, frameNumberRequestPair.getKey(),
686 completedFrameNumber));
687 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700688 }
689 }
690 iter.remove();
691
692 // Call onCaptureSequenceCompleted
693 if (holder != null) {
694 Runnable resultDispatch = new Runnable() {
695 @Override
696 public void run() {
697 if (!CameraDevice.this.isClosed()){
698 if (DEBUG) {
699 Log.d(TAG, String.format(
700 "fire sequence complete for request %d",
701 requestId));
702 }
703
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700704 long lastFrameNumber = frameNumberRequestPair.getKey();
705 if (lastFrameNumber < Integer.MIN_VALUE
706 || lastFrameNumber > Integer.MAX_VALUE) {
707 throw new AssertionError(lastFrameNumber
708 + " cannot be cast to int");
709 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700710 holder.getListener().onCaptureSequenceCompleted(
711 CameraDevice.this,
712 requestId,
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700713 (int)lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700714 }
715 }
716 };
717 holder.getHandler().post(resultDispatch);
718 }
719
720 }
721 }
722 }
723
Zhijun Heecb323e2013-07-31 09:40:27 -0700724 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000725
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700726 //
727 // Constants below need to be kept up-to-date with
728 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
729 //
730
731 //
732 // Error codes for onCameraError
733 //
734
735 /**
736 * Camera has been disconnected
737 */
738 static final int ERROR_CAMERA_DISCONNECTED = 0;
739
740 /**
741 * Camera has encountered a device-level error
742 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
743 */
744 static final int ERROR_CAMERA_DEVICE = 1;
745
746 /**
747 * Camera has encountered a service-level error
748 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
749 */
750 static final int ERROR_CAMERA_SERVICE = 2;
751
Igor Murashkin70725502013-06-25 20:27:06 +0000752 @Override
753 public IBinder asBinder() {
754 return this;
755 }
756
Igor Murashkin70725502013-06-25 20:27:06 +0000757 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700758 public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700759 Runnable r = null;
760 if (isClosed()) return;
761
762 synchronized(mLock) {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700763 switch (errorCode) {
764 case ERROR_CAMERA_DISCONNECTED:
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700765 r = mCallOnDisconnected;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700766 break;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700767 default:
768 Log.e(TAG, "Unknown error from camera device: " + errorCode);
769 // no break
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700770 case ERROR_CAMERA_DEVICE:
771 case ERROR_CAMERA_SERVICE:
772 r = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700773 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700774 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700775 if (!CameraDevice.this.isClosed()) {
776 mDeviceListener.onError(CameraDevice.this, errorCode);
777 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700778 }
779 };
780 break;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700781 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700782 CameraDevice.this.mDeviceHandler.post(r);
Zhijun Heecb323e2013-07-31 09:40:27 -0700783 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700784
785 // Fire onCaptureSequenceCompleted
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700786 if (DEBUG) {
787 Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
788 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700789 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
790 checkAndFireSequenceComplete();
791
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700792 }
793
794 @Override
795 public void onCameraIdle() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700796 if (isClosed()) return;
797
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700798 if (DEBUG) {
799 Log.d(TAG, "Camera now idle");
800 }
801 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700802 if (!CameraDevice.this.mIdle) {
803 CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
804 }
805 CameraDevice.this.mIdle = true;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700806 }
807 }
808
809 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700810 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
811 int requestId = resultExtras.getRequestId();
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700812 if (DEBUG) {
813 Log.d(TAG, "Capture started for id " + requestId);
814 }
815 final CaptureListenerHolder holder;
816
817 // Get the listener for this frame ID, if there is one
818 synchronized (mLock) {
819 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
820 }
821
822 if (holder == null) {
823 return;
824 }
825
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700826 if (isClosed()) return;
827
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700828 // Dispatch capture start notice
829 holder.getHandler().post(
830 new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700831 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700832 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700833 if (!CameraDevice.this.isClosed()) {
834 holder.getListener().onCaptureStarted(
835 CameraDevice.this,
Jianing Weid2c3a822014-03-27 18:27:43 -0700836 holder.getRequest(resultExtras.getSubsequenceId()),
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700837 timestamp);
838 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700839 }
840 });
Igor Murashkin70725502013-06-25 20:27:06 +0000841 }
842
843 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700844 public void onResultReceived(CameraMetadataNative result,
845 CaptureResultExtras resultExtras) throws RemoteException {
846 int requestId = resultExtras.getRequestId();
Zhijun Heecb323e2013-07-31 09:40:27 -0700847 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700848 Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
849 + requestId);
Zhijun Heecb323e2013-07-31 09:40:27 -0700850 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700851 final CaptureListenerHolder holder;
852 synchronized (mLock) {
853 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
854 }
Igor Murashkin70725502013-06-25 20:27:06 +0000855
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800856 Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
857 boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
858
Jianing Weibaf0c652014-04-18 17:35:00 -0700859 // Update tracker (increment counter) when it's not a partial result.
860 if (!quirkIsPartialResult) {
861 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
862 }
863
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700864 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000865 if (holder == null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700866 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700867 Log.d(TAG,
868 "holder is null, early return at frame "
869 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700870 }
Igor Murashkin70725502013-06-25 20:27:06 +0000871 return;
872 }
873
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700874 if (isClosed()) {
875 if (DEBUG) {
Jianing Weibaf0c652014-04-18 17:35:00 -0700876 Log.d(TAG,
877 "camera is closed, early return at frame "
878 + resultExtras.getFrameNumber());
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700879 }
880 return;
881 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700882
Jianing Weid2c3a822014-03-27 18:27:43 -0700883 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700884 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000885
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800886 Runnable resultDispatch = null;
887
888 // Either send a partial result or the final capture completed result
889 if (quirkIsPartialResult) {
890 // Partial result
891 resultDispatch = new Runnable() {
892 @Override
893 public void run() {
894 if (!CameraDevice.this.isClosed()){
895 holder.getListener().onCapturePartial(
896 CameraDevice.this,
897 request,
898 resultAsCapture);
899 }
900 }
901 };
902 } else {
903 // Final capture result
904 resultDispatch = new Runnable() {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700905 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700906 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700907 if (!CameraDevice.this.isClosed()){
908 holder.getListener().onCaptureCompleted(
909 CameraDevice.this,
910 request,
911 resultAsCapture);
912 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700913 }
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800914 };
915 }
916
917 holder.getHandler().post(resultDispatch);
Jianing Weid2c3a822014-03-27 18:27:43 -0700918
919 // Fire onCaptureSequenceCompleted
920 if (!quirkIsPartialResult) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700921 checkAndFireSequenceComplete();
922 }
Igor Murashkin70725502013-06-25 20:27:06 +0000923 }
924
925 }
926
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700927 /**
928 * Default handler management. If handler is null, get the current thread's
929 * Looper to create a Handler with. If no looper exists, throw exception.
930 */
931 private Handler checkHandler(Handler handler) {
932 if (handler == null) {
933 Looper looper = Looper.myLooper();
934 if (looper == null) {
935 throw new IllegalArgumentException(
936 "No handler given, and current thread has no looper!");
937 }
938 handler = new Handler(looper);
939 }
940 return handler;
941 }
942
Zhijun He7f4d3142013-07-23 07:54:38 -0700943 private void checkIfCameraClosed() {
944 if (mRemoteDevice == null) {
945 throw new IllegalStateException("CameraDevice was already closed");
946 }
947 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700948
949 private boolean isClosed() {
950 synchronized(mLock) {
951 return (mRemoteDevice == null);
952 }
953 }
Igor Murashkin70725502013-06-25 20:27:06 +0000954}