blob: 7328fe354c17545f5b94b1c7c0975653e2031b0a [file] [log] [blame]
Igor Murashkin70725502013-06-25 20:27:06 +00001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070017package android.hardware.camera2.impl;
Igor Murashkin70725502013-06-25 20:27:06 +000018
Igor Murashkin57ea59b2013-08-23 16:55:57 -070019import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
20
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070021import android.hardware.camera2.CameraAccessException;
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070022import android.hardware.camera2.CaptureRequest;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070023import android.hardware.camera2.CaptureResult;
Jianing Weid2c3a822014-03-27 18:27:43 -070024import android.hardware.camera2.CaptureResultExtras;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070025import android.hardware.camera2.ICameraDeviceCallbacks;
26import android.hardware.camera2.ICameraDeviceUser;
Jianing Weid2c3a822014-03-27 18:27:43 -070027import android.hardware.camera2.LongParcelable;
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -070028import android.hardware.camera2.utils.CameraBinderDecorator;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070029import android.hardware.camera2.utils.CameraRuntimeException;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070030import android.os.Handler;
Ruben Brunkdecfe952013-10-29 11:00:32 -070031import android.os.IBinder;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070032import android.os.Looper;
Ruben Brunkdecfe952013-10-29 11:00:32 -070033import android.os.RemoteException;
Igor Murashkin70725502013-06-25 20:27:06 +000034import android.util.Log;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070035import android.util.SparseArray;
Igor Murashkin70725502013-06-25 20:27:06 +000036import android.view.Surface;
37
Jianing Weid2c3a822014-03-27 18:27:43 -070038import java.util.AbstractMap.SimpleEntry;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070039import java.util.ArrayList;
Igor Murashkin57ea59b2013-08-23 16:55:57 -070040import java.util.HashSet;
Ruben Brunkdecfe952013-10-29 11:00:32 -070041import java.util.Iterator;
Igor Murashkin70725502013-06-25 20:27:06 +000042import java.util.List;
Jianing Weid2c3a822014-03-27 18:27:43 -070043import java.util.TreeSet;
Igor Murashkin70725502013-06-25 20:27:06 +000044
45/**
Eino-Ville Talvala70c22072013-08-27 12:09:04 -070046 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
Igor Murashkin70725502013-06-25 20:27:06 +000047 */
Eino-Ville Talvala2f1a2e42013-07-25 17:12:05 -070048public class CameraDevice implements android.hardware.camera2.CameraDevice {
Igor Murashkin70725502013-06-25 20:27:06 +000049
50 private final String TAG;
Zhijun Heecb323e2013-07-31 09:40:27 -070051 private final boolean DEBUG;
Igor Murashkin70725502013-06-25 20:27:06 +000052
Ruben Brunkdecfe952013-10-29 11:00:32 -070053 private static final int REQUEST_ID_NONE = -1;
54
Igor Murashkin70725502013-06-25 20:27:06 +000055 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
56 private ICameraDeviceUser mRemoteDevice;
57
58 private final Object mLock = new Object();
59 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
60
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070061 private final StateListener mDeviceListener;
62 private final Handler mDeviceHandler;
63
64 private boolean mIdle = true;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -070065
66 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
67 new SparseArray<CaptureListenerHolder>();
Igor Murashkin70725502013-06-25 20:27:06 +000068
Ruben Brunkdecfe952013-10-29 11:00:32 -070069 private int mRepeatingRequestId = REQUEST_ID_NONE;
70 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
Igor Murashkin57ea59b2013-08-23 16:55:57 -070071 // Map stream IDs to Surfaces
72 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
Igor Murashkin70725502013-06-25 20:27:06 +000073
74 private final String mCameraId;
75
Jianing Weid2c3a822014-03-27 18:27:43 -070076 /**
77 * A list tracking request and its expected last frame.
78 * Updated when calling ICameraDeviceUser methods.
79 */
80 private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
81 mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
82
83 /**
84 * An object tracking received frame numbers.
85 * Updated when receiving callbacks from ICameraDeviceCallbacks.
86 */
87 private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
88
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070089 // Runnables for all state transitions, except error, which needs the
90 // error code argument
91
92 private final Runnable mCallOnOpened = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -070093 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -070094 public void run() {
95 if (!CameraDevice.this.isClosed()) {
96 mDeviceListener.onOpened(CameraDevice.this);
97 }
98 }
99 };
100
101 private final Runnable mCallOnUnconfigured = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700102 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700103 public void run() {
104 if (!CameraDevice.this.isClosed()) {
105 mDeviceListener.onUnconfigured(CameraDevice.this);
106 }
107 }
108 };
109
110 private final Runnable mCallOnActive = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700111 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700112 public void run() {
113 if (!CameraDevice.this.isClosed()) {
114 mDeviceListener.onActive(CameraDevice.this);
115 }
116 }
117 };
118
119 private final Runnable mCallOnBusy = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700120 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700121 public void run() {
122 if (!CameraDevice.this.isClosed()) {
123 mDeviceListener.onBusy(CameraDevice.this);
124 }
125 }
126 };
127
128 private final Runnable mCallOnClosed = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700129 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700130 public void run() {
Zhijun He2db56022014-01-02 15:12:00 -0800131 mDeviceListener.onClosed(CameraDevice.this);
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700132 }
133 };
134
135 private final Runnable mCallOnIdle = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700136 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700137 public void run() {
138 if (!CameraDevice.this.isClosed()) {
139 mDeviceListener.onIdle(CameraDevice.this);
140 }
141 }
142 };
143
144 private final Runnable mCallOnDisconnected = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700145 @Override
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700146 public void run() {
147 if (!CameraDevice.this.isClosed()) {
148 mDeviceListener.onDisconnected(CameraDevice.this);
149 }
150 }
151 };
152
153 public CameraDevice(String cameraId, StateListener listener, Handler handler) {
154 if (cameraId == null || listener == null || handler == null) {
155 throw new IllegalArgumentException("Null argument given");
156 }
Igor Murashkin70725502013-06-25 20:27:06 +0000157 mCameraId = cameraId;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700158 mDeviceListener = listener;
159 mDeviceHandler = handler;
Igor Murashkin70725502013-06-25 20:27:06 +0000160 TAG = String.format("CameraDevice-%s-JV", mCameraId);
Zhijun Heecb323e2013-07-31 09:40:27 -0700161 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Igor Murashkin70725502013-06-25 20:27:06 +0000162 }
163
164 public CameraDeviceCallbacks getCallbacks() {
165 return mCallbacks;
166 }
167
Igor Murashkin70725502013-06-25 20:27:06 +0000168 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
Eino-Ville Talvala5afd3e92013-08-21 10:37:04 -0700169 // TODO: Move from decorator to direct binder-mediated exceptions
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700170 synchronized(mLock) {
171 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
172
173 mDeviceHandler.post(mCallOnOpened);
174 mDeviceHandler.post(mCallOnUnconfigured);
175 }
Igor Murashkin70725502013-06-25 20:27:06 +0000176 }
177
178 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700179 public String getId() {
180 return mCameraId;
181 }
182
183 @Override
Igor Murashkin70725502013-06-25 20:27:06 +0000184 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700185 // Treat a null input the same an empty list
186 if (outputs == null) {
187 outputs = new ArrayList<Surface>();
188 }
Igor Murashkin70725502013-06-25 20:27:06 +0000189 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700190 checkIfCameraClosed();
191
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700192 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
193 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
194
195 // Determine which streams need to be created, which to be deleted
196 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
197 int streamId = mConfiguredOutputs.keyAt(i);
198 Surface s = mConfiguredOutputs.valueAt(i);
199
200 if (!outputs.contains(s)) {
201 deleteList.add(streamId);
202 } else {
203 addSet.remove(s); // Don't create a stream previously created
204 }
205 }
206
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700207 mDeviceHandler.post(mCallOnBusy);
208 stopRepeating();
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700209
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700210 try {
Ruben Brunkdecfe952013-10-29 11:00:32 -0700211 waitUntilIdle();
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700212
213 // TODO: mRemoteDevice.beginConfigure
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700214 // Delete all streams first (to free up HW resources)
215 for (Integer streamId : deleteList) {
216 mRemoteDevice.deleteStream(streamId);
217 mConfiguredOutputs.delete(streamId);
218 }
219
220 // Add all new streams
221 for (Surface s : addSet) {
Igor Murashkin70725502013-06-25 20:27:06 +0000222 // TODO: remove width,height,format since we are ignoring
223 // it.
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700224 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
225 mConfiguredOutputs.put(streamId, s);
Igor Murashkin70725502013-06-25 20:27:06 +0000226 }
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700227
228 // TODO: mRemoteDevice.endConfigure
229 } catch (CameraRuntimeException e) {
230 if (e.getReason() == CAMERA_IN_USE) {
231 throw new IllegalStateException("The camera is currently busy." +
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700232 " You must wait until the previous operation completes.");
Igor Murashkin57ea59b2013-08-23 16:55:57 -0700233 }
234
235 throw e.asChecked();
236 } catch (RemoteException e) {
237 // impossible
238 return;
Igor Murashkin70725502013-06-25 20:27:06 +0000239 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700240
241 if (outputs.size() > 0) {
242 mDeviceHandler.post(mCallOnIdle);
243 } else {
244 mDeviceHandler.post(mCallOnUnconfigured);
245 }
Igor Murashkin70725502013-06-25 20:27:06 +0000246 }
247 }
248
249 @Override
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700250 public CaptureRequest.Builder createCaptureRequest(int templateType)
251 throws CameraAccessException {
Igor Murashkin70725502013-06-25 20:27:06 +0000252 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700253 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000254
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700255 CameraMetadataNative templatedRequest = new CameraMetadataNative();
Igor Murashkin70725502013-06-25 20:27:06 +0000256
257 try {
258 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
259 } catch (CameraRuntimeException e) {
260 throw e.asChecked();
261 } catch (RemoteException e) {
262 // impossible
263 return null;
264 }
265
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700266 CaptureRequest.Builder builder =
267 new CaptureRequest.Builder(templatedRequest);
Igor Murashkin70725502013-06-25 20:27:06 +0000268
Eino-Ville Talvala70c22072013-08-27 12:09:04 -0700269 return builder;
Igor Murashkin70725502013-06-25 20:27:06 +0000270 }
271 }
272
273 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700274 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
Igor Murashkin70725502013-06-25 20:27:06 +0000275 throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700276 if (DEBUG) {
277 Log.d(TAG, "calling capture");
278 }
279 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
280 requestList.add(request);
281 return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000282 }
283
284 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700285 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700286 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700287 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700288 if (requests.isEmpty()) {
289 Log.w(TAG, "Capture burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700290 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700291 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700292 return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
Igor Murashkin70725502013-06-25 20:27:06 +0000293 }
294
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700295 /**
296 * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
297 * starting and stopping repeating request and flushing.
298 *
299 * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
300 * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
301 * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
302 * is added to the list mFrameNumberRequestPairs.</p>
303 *
304 * @param requestId the request ID of the current repeating request.
305 *
306 * @param lastFrameNumber last frame number returned from binder.
307 */
308 private void checkEarlyTriggerSequenceComplete(
309 final int requestId, final long lastFrameNumber) {
310 // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
311 // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
312 if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
313 final CaptureListenerHolder holder;
314 int index = mCaptureListenerMap.indexOfKey(requestId);
315 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
316 if (holder != null) {
317 mCaptureListenerMap.removeAt(index);
318 }
319
320 if (holder != null) {
321 if (DEBUG) {
322 Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
323 + " request did not reach HAL");
324 }
325
326 Runnable resultDispatch = new Runnable() {
327 @Override
328 public void run() {
329 if (!CameraDevice.this.isClosed()) {
330 if (DEBUG) {
331 Log.d(TAG, String.format(
332 "early trigger sequence complete for request %d",
333 requestId));
334 }
335 if (lastFrameNumber < Integer.MIN_VALUE
336 || lastFrameNumber > Integer.MAX_VALUE) {
337 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
338 }
339 holder.getListener().onCaptureSequenceCompleted(
340 CameraDevice.this,
341 requestId,
342 (int)lastFrameNumber);
343 }
344 }
345 };
346 holder.getHandler().post(resultDispatch);
347 } else {
348 Log.w(TAG, String.format(
349 "did not register listener to request %d",
350 requestId));
351 }
352 } else {
353 mFrameNumberRequestPairs.add(
354 new SimpleEntry<Long, Integer>(lastFrameNumber,
355 requestId));
356 }
357 }
358
Jianing Weid2c3a822014-03-27 18:27:43 -0700359 private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700360 Handler handler, boolean repeating) throws CameraAccessException {
361
362 // Need a valid handler, or current thread needs to have a looper, if
363 // listener is valid
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700364 if (listener != null) {
365 handler = checkHandler(handler);
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700366 }
Igor Murashkin70725502013-06-25 20:27:06 +0000367
368 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700369 checkIfCameraClosed();
Igor Murashkin70725502013-06-25 20:27:06 +0000370 int requestId;
371
Ruben Brunke73b41b2013-11-07 19:30:43 -0800372 if (repeating) {
373 stopRepeating();
374 }
375
Jianing Weid2c3a822014-03-27 18:27:43 -0700376 LongParcelable lastFrameNumberRef = new LongParcelable();
Igor Murashkin70725502013-06-25 20:27:06 +0000377 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700378 requestId = mRemoteDevice.submitRequestList(requestList, repeating,
379 /*out*/lastFrameNumberRef);
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700380 if (DEBUG) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700381 Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
382 }
Igor Murashkin70725502013-06-25 20:27:06 +0000383 } catch (CameraRuntimeException e) {
384 throw e.asChecked();
385 } catch (RemoteException e) {
386 // impossible
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700387 return -1;
Igor Murashkin70725502013-06-25 20:27:06 +0000388 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700389
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700390 if (listener != null) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700391 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
392 requestList, handler, repeating));
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700393 }
Igor Murashkin70725502013-06-25 20:27:06 +0000394
Jianing Weid2c3a822014-03-27 18:27:43 -0700395 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700396
Igor Murashkin70725502013-06-25 20:27:06 +0000397 if (repeating) {
Jianing Weid2c3a822014-03-27 18:27:43 -0700398 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700399 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700400 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700401 mRepeatingRequestId = requestId;
Jianing Weid2c3a822014-03-27 18:27:43 -0700402 } else {
403 mFrameNumberRequestPairs.add(
404 new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
Igor Murashkin70725502013-06-25 20:27:06 +0000405 }
406
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700407 if (mIdle) {
408 mDeviceHandler.post(mCallOnActive);
409 }
410 mIdle = false;
411
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700412 return requestId;
Igor Murashkin70725502013-06-25 20:27:06 +0000413 }
414 }
415
416 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700417 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700418 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700419 List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
420 requestList.add(request);
421 return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000422 }
423
424 @Override
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700425 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700426 Handler handler) throws CameraAccessException {
Jianing Weid2c3a822014-03-27 18:27:43 -0700427 // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700428 if (requests.isEmpty()) {
429 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700430 return -1;
Zhijun Hefc19e2c2013-08-22 14:43:07 -0700431 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700432 return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
Igor Murashkin70725502013-06-25 20:27:06 +0000433 }
434
435 @Override
436 public void stopRepeating() throws CameraAccessException {
437
438 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700439 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700440 if (mRepeatingRequestId != REQUEST_ID_NONE) {
441
442 int requestId = mRepeatingRequestId;
443 mRepeatingRequestId = REQUEST_ID_NONE;
444
445 // Queue for deletion after in-flight requests finish
Zhijun He1a9b6462014-03-31 16:11:33 -0700446 if (mCaptureListenerMap.get(requestId) != null) {
447 mRepeatingRequestIdDeletedList.add(requestId);
448 }
Igor Murashkin70725502013-06-25 20:27:06 +0000449
450 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700451 LongParcelable lastFrameNumberRef = new LongParcelable();
452 mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
453 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700454
455 checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
456
Igor Murashkin70725502013-06-25 20:27:06 +0000457 } catch (CameraRuntimeException e) {
458 throw e.asChecked();
459 } catch (RemoteException e) {
460 // impossible
461 return;
462 }
463 }
464 }
465 }
466
Zhijun Hed842fcd2013-12-26 14:14:04 -0800467 private void waitUntilIdle() throws CameraAccessException {
Zhijun He7f4d3142013-07-23 07:54:38 -0700468
469 synchronized (mLock) {
470 checkIfCameraClosed();
Ruben Brunkdecfe952013-10-29 11:00:32 -0700471 if (mRepeatingRequestId != REQUEST_ID_NONE) {
Zhijun He7f4d3142013-07-23 07:54:38 -0700472 throw new IllegalStateException("Active repeating request ongoing");
473 }
474
475 try {
476 mRemoteDevice.waitUntilIdle();
477 } catch (CameraRuntimeException e) {
478 throw e.asChecked();
479 } catch (RemoteException e) {
480 // impossible
481 return;
482 }
Ruben Brunkdecfe952013-10-29 11:00:32 -0700483
484 mRepeatingRequestId = REQUEST_ID_NONE;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700485 }
Igor Murashkin70725502013-06-25 20:27:06 +0000486 }
487
488 @Override
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700489 public void flush() throws CameraAccessException {
490 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700491 checkIfCameraClosed();
492
493 mDeviceHandler.post(mCallOnBusy);
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700494 try {
Jianing Weid2c3a822014-03-27 18:27:43 -0700495 LongParcelable lastFrameNumberRef = new LongParcelable();
496 mRemoteDevice.flush(/*out*/lastFrameNumberRef);
497 if (mRepeatingRequestId != REQUEST_ID_NONE) {
498 long lastFrameNumber = lastFrameNumberRef.getNumber();
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700499 checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700500 mRepeatingRequestId = REQUEST_ID_NONE;
501 }
Eino-Ville Talvala8ebd52b2013-08-13 12:09:44 -0700502 } catch (CameraRuntimeException e) {
503 throw e.asChecked();
504 } catch (RemoteException e) {
505 // impossible
506 return;
507 }
508 }
509 }
510
511 @Override
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700512 public void close() {
Igor Murashkin70725502013-06-25 20:27:06 +0000513 synchronized (mLock) {
514
515 try {
Igor Murashkin2a3eced2013-08-28 17:35:10 -0700516 if (mRemoteDevice != null) {
517 mRemoteDevice.disconnect();
518 }
Igor Murashkin70725502013-06-25 20:27:06 +0000519 } catch (CameraRuntimeException e) {
Igor Murashkin5c9eaf62013-09-10 19:35:24 -0700520 Log.e(TAG, "Exception while closing: ", e.asChecked());
Igor Murashkin70725502013-06-25 20:27:06 +0000521 } catch (RemoteException e) {
522 // impossible
523 }
524
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700525 if (mRemoteDevice != null) {
526 mDeviceHandler.post(mCallOnClosed);
527 }
Igor Murashkin70725502013-06-25 20:27:06 +0000528
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700529 mRemoteDevice = null;
Igor Murashkin70725502013-06-25 20:27:06 +0000530 }
531 }
532
533 @Override
534 protected void finalize() throws Throwable {
535 try {
536 close();
Igor Murashkin70725502013-06-25 20:27:06 +0000537 }
538 finally {
539 super.finalize();
540 }
541 }
542
543 static class CaptureListenerHolder {
544
545 private final boolean mRepeating;
546 private final CaptureListener mListener;
Jianing Weid2c3a822014-03-27 18:27:43 -0700547 private final List<CaptureRequest> mRequestList;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700548 private final Handler mHandler;
Igor Murashkin70725502013-06-25 20:27:06 +0000549
Jianing Weid2c3a822014-03-27 18:27:43 -0700550 CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
551 Handler handler, boolean repeating) {
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700552 if (listener == null || handler == null) {
553 throw new UnsupportedOperationException(
554 "Must have a valid handler and a valid listener");
555 }
Igor Murashkin70725502013-06-25 20:27:06 +0000556 mRepeating = repeating;
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700557 mHandler = handler;
Jianing Weid2c3a822014-03-27 18:27:43 -0700558 mRequestList = new ArrayList<CaptureRequest>(requestList);
Igor Murashkin70725502013-06-25 20:27:06 +0000559 mListener = listener;
560 }
561
562 public boolean isRepeating() {
563 return mRepeating;
564 }
565
566 public CaptureListener getListener() {
567 return mListener;
568 }
569
Jianing Weid2c3a822014-03-27 18:27:43 -0700570 public CaptureRequest getRequest(int subsequenceId) {
571 if (subsequenceId >= mRequestList.size()) {
572 throw new IllegalArgumentException(
573 String.format(
574 "Requested subsequenceId %d is larger than request list size %d.",
575 subsequenceId, mRequestList.size()));
576 } else {
577 if (subsequenceId < 0) {
578 throw new IllegalArgumentException(String.format(
579 "Requested subsequenceId %d is negative", subsequenceId));
580 } else {
581 return mRequestList.get(subsequenceId);
582 }
583 }
584 }
585
Igor Murashkin70725502013-06-25 20:27:06 +0000586 public CaptureRequest getRequest() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700587 return getRequest(0);
Igor Murashkin70725502013-06-25 20:27:06 +0000588 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700589
590 public Handler getHandler() {
591 return mHandler;
592 }
593
Igor Murashkin70725502013-06-25 20:27:06 +0000594 }
595
Jianing Weid2c3a822014-03-27 18:27:43 -0700596 /**
597 * This class tracks the last frame number for submitted requests.
598 */
599 public class FrameNumberTracker {
600
601 private long mCompletedFrameNumber = -1;
602 private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
603
604 private void update() {
605 Iterator<Long> iter = mFutureErrorSet.iterator();
606 while (iter.hasNext()) {
607 long errorFrameNumber = iter.next();
608 if (errorFrameNumber == mCompletedFrameNumber + 1) {
609 mCompletedFrameNumber++;
610 iter.remove();
611 } else {
612 break;
613 }
614 }
615 }
616
617 /**
618 * This function is called every time when a result or an error is received.
619 * @param frameNumber: the frame number corresponding to the result or error
620 * @param isError: true if it is an error, false if it is not an error
621 */
622 public void updateTracker(long frameNumber, boolean isError) {
623 if (isError) {
624 mFutureErrorSet.add(frameNumber);
625 } else {
626 /**
627 * HAL cannot send an OnResultReceived for frame N unless it knows for
628 * sure that all frames prior to N have either errored out or completed.
629 * So if the current frame is not an error, then all previous frames
630 * should have arrived. The following line checks whether this holds.
631 */
632 if (frameNumber != mCompletedFrameNumber + 1) {
633 throw new AssertionError(String.format(
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700634 "result frame number %d comes out of order, should be %d + 1",
635 frameNumber, mCompletedFrameNumber));
Jianing Weid2c3a822014-03-27 18:27:43 -0700636 }
637 mCompletedFrameNumber++;
638 }
639 update();
640 }
641
642 public long getCompletedFrameNumber() {
643 return mCompletedFrameNumber;
644 }
645
646 }
647
648 private void checkAndFireSequenceComplete() {
649 long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
650 Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
651 while (iter.hasNext()) {
652 final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
653 if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
654
655 // remove request from mCaptureListenerMap
656 final int requestId = frameNumberRequestPair.getValue();
657 final CaptureListenerHolder holder;
658 synchronized (mLock) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700659 int index = mCaptureListenerMap.indexOfKey(requestId);
660 holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
Jianing Weid2c3a822014-03-27 18:27:43 -0700661 : null;
662 if (holder != null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700663 mCaptureListenerMap.removeAt(index);
664 if (DEBUG) {
665 Log.v(TAG, String.format(
666 "remove holder for requestId %d, "
667 + "because lastFrame %d is <= %d",
668 requestId, frameNumberRequestPair.getKey(),
669 completedFrameNumber));
670 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700671 }
672 }
673 iter.remove();
674
675 // Call onCaptureSequenceCompleted
676 if (holder != null) {
677 Runnable resultDispatch = new Runnable() {
678 @Override
679 public void run() {
680 if (!CameraDevice.this.isClosed()){
681 if (DEBUG) {
682 Log.d(TAG, String.format(
683 "fire sequence complete for request %d",
684 requestId));
685 }
686
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700687 long lastFrameNumber = frameNumberRequestPair.getKey();
688 if (lastFrameNumber < Integer.MIN_VALUE
689 || lastFrameNumber > Integer.MAX_VALUE) {
690 throw new AssertionError(lastFrameNumber
691 + " cannot be cast to int");
692 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700693 holder.getListener().onCaptureSequenceCompleted(
694 CameraDevice.this,
695 requestId,
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700696 (int)lastFrameNumber);
Jianing Weid2c3a822014-03-27 18:27:43 -0700697 }
698 }
699 };
700 holder.getHandler().post(resultDispatch);
701 }
702
703 }
704 }
705 }
706
Zhijun Heecb323e2013-07-31 09:40:27 -0700707 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
Igor Murashkin70725502013-06-25 20:27:06 +0000708
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700709 //
710 // Constants below need to be kept up-to-date with
711 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
712 //
713
714 //
715 // Error codes for onCameraError
716 //
717
718 /**
719 * Camera has been disconnected
720 */
721 static final int ERROR_CAMERA_DISCONNECTED = 0;
722
723 /**
724 * Camera has encountered a device-level error
725 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
726 */
727 static final int ERROR_CAMERA_DEVICE = 1;
728
729 /**
730 * Camera has encountered a service-level error
731 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
732 */
733 static final int ERROR_CAMERA_SERVICE = 2;
734
Igor Murashkin70725502013-06-25 20:27:06 +0000735 @Override
736 public IBinder asBinder() {
737 return this;
738 }
739
Igor Murashkin70725502013-06-25 20:27:06 +0000740 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700741 public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700742 Runnable r = null;
743 if (isClosed()) return;
744
745 synchronized(mLock) {
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700746 switch (errorCode) {
747 case ERROR_CAMERA_DISCONNECTED:
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700748 r = mCallOnDisconnected;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700749 break;
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700750 default:
751 Log.e(TAG, "Unknown error from camera device: " + errorCode);
752 // no break
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700753 case ERROR_CAMERA_DEVICE:
754 case ERROR_CAMERA_SERVICE:
755 r = new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700756 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700757 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700758 if (!CameraDevice.this.isClosed()) {
759 mDeviceListener.onError(CameraDevice.this, errorCode);
760 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700761 }
762 };
763 break;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700764 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700765 CameraDevice.this.mDeviceHandler.post(r);
Zhijun Heecb323e2013-07-31 09:40:27 -0700766 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700767
768 // Fire onCaptureSequenceCompleted
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700769 if (DEBUG) {
770 Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
771 }
Jianing Weid2c3a822014-03-27 18:27:43 -0700772 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
773 checkAndFireSequenceComplete();
774
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700775 }
776
777 @Override
778 public void onCameraIdle() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700779 if (isClosed()) return;
780
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700781 if (DEBUG) {
782 Log.d(TAG, "Camera now idle");
783 }
784 synchronized (mLock) {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700785 if (!CameraDevice.this.mIdle) {
786 CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
787 }
788 CameraDevice.this.mIdle = true;
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700789 }
790 }
791
792 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700793 public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
794 int requestId = resultExtras.getRequestId();
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700795 if (DEBUG) {
796 Log.d(TAG, "Capture started for id " + requestId);
797 }
798 final CaptureListenerHolder holder;
799
800 // Get the listener for this frame ID, if there is one
801 synchronized (mLock) {
802 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
803 }
804
805 if (holder == null) {
806 return;
807 }
808
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700809 if (isClosed()) return;
810
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700811 // Dispatch capture start notice
812 holder.getHandler().post(
813 new Runnable() {
Jianing Weid2c3a822014-03-27 18:27:43 -0700814 @Override
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700815 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700816 if (!CameraDevice.this.isClosed()) {
817 holder.getListener().onCaptureStarted(
818 CameraDevice.this,
Jianing Weid2c3a822014-03-27 18:27:43 -0700819 holder.getRequest(resultExtras.getSubsequenceId()),
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700820 timestamp);
821 }
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700822 }
823 });
Igor Murashkin70725502013-06-25 20:27:06 +0000824 }
825
826 @Override
Jianing Weid2c3a822014-03-27 18:27:43 -0700827 public void onResultReceived(CameraMetadataNative result,
828 CaptureResultExtras resultExtras) throws RemoteException {
829 int requestId = resultExtras.getRequestId();
Zhijun Heecb323e2013-07-31 09:40:27 -0700830 if (DEBUG) {
831 Log.d(TAG, "Received result for id " + requestId);
832 }
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700833 final CaptureListenerHolder holder;
834 synchronized (mLock) {
835 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
836 }
Igor Murashkin70725502013-06-25 20:27:06 +0000837
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800838 Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
839 boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
840
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700841 // Check if we have a listener for this
Igor Murashkin70725502013-06-25 20:27:06 +0000842 if (holder == null) {
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700843 if (DEBUG) {
844 Log.v(TAG, "holder is null, early return");
845 }
Igor Murashkin70725502013-06-25 20:27:06 +0000846 return;
847 }
848
Jianing Wei5a4b02b2014-04-11 09:59:24 -0700849 if (isClosed()) {
850 if (DEBUG) {
851 Log.v(TAG, "camera is closed, early return");
852 }
853 return;
854 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700855
Jianing Weid2c3a822014-03-27 18:27:43 -0700856 final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700857 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
Igor Murashkin70725502013-06-25 20:27:06 +0000858
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800859 Runnable resultDispatch = null;
860
861 // Either send a partial result or the final capture completed result
862 if (quirkIsPartialResult) {
863 // Partial result
864 resultDispatch = new Runnable() {
865 @Override
866 public void run() {
867 if (!CameraDevice.this.isClosed()){
868 holder.getListener().onCapturePartial(
869 CameraDevice.this,
870 request,
871 resultAsCapture);
872 }
873 }
874 };
875 } else {
876 // Final capture result
877 resultDispatch = new Runnable() {
Igor Murashkin6bbf9dc2013-09-05 12:22:00 -0700878 @Override
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700879 public void run() {
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700880 if (!CameraDevice.this.isClosed()){
881 holder.getListener().onCaptureCompleted(
882 CameraDevice.this,
883 request,
884 resultAsCapture);
885 }
Eino-Ville Talvala4af73c22013-08-14 10:35:46 -0700886 }
Eino-Ville Talvala7a313102013-11-07 14:45:06 -0800887 };
888 }
889
890 holder.getHandler().post(resultDispatch);
Jianing Weid2c3a822014-03-27 18:27:43 -0700891
892 // Fire onCaptureSequenceCompleted
893 if (!quirkIsPartialResult) {
894 mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
895 checkAndFireSequenceComplete();
896 }
Igor Murashkin70725502013-06-25 20:27:06 +0000897 }
898
899 }
900
Eino-Ville Talvalae841d4e2013-09-05 09:04:08 -0700901 /**
902 * Default handler management. If handler is null, get the current thread's
903 * Looper to create a Handler with. If no looper exists, throw exception.
904 */
905 private Handler checkHandler(Handler handler) {
906 if (handler == null) {
907 Looper looper = Looper.myLooper();
908 if (looper == null) {
909 throw new IllegalArgumentException(
910 "No handler given, and current thread has no looper!");
911 }
912 handler = new Handler(looper);
913 }
914 return handler;
915 }
916
Zhijun He7f4d3142013-07-23 07:54:38 -0700917 private void checkIfCameraClosed() {
918 if (mRemoteDevice == null) {
919 throw new IllegalStateException("CameraDevice was already closed");
920 }
921 }
Eino-Ville Talvala868d9042013-10-03 11:15:21 -0700922
923 private boolean isClosed() {
924 synchronized(mLock) {
925 return (mRemoteDevice == null);
926 }
927 }
Igor Murashkin70725502013-06-25 20:27:06 +0000928}