2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.hardware.camera2.impl;
19 import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
21 import android.hardware.camera2.CameraAccessException;
22 import android.hardware.camera2.CaptureRequest;
23 import android.hardware.camera2.CaptureResult;
24 import android.hardware.camera2.ICameraDeviceCallbacks;
25 import android.hardware.camera2.ICameraDeviceUser;
26 import android.hardware.camera2.utils.CameraBinderDecorator;
27 import android.hardware.camera2.utils.CameraRuntimeException;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Looper;
31 import android.os.RemoteException;
32 import android.util.Log;
33 import android.util.SparseArray;
34 import android.view.Surface;
36 import java.util.ArrayList;
37 import java.util.HashSet;
38 import java.util.Iterator;
39 import java.util.List;
42 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
44 public class CameraDevice implements android.hardware.camera2.CameraDevice {
46 private final String TAG;
47 private final boolean DEBUG;
49 private static final int REQUEST_ID_NONE = -1;
51 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
52 private ICameraDeviceUser mRemoteDevice;
54 private final Object mLock = new Object();
55 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
57 private final StateListener mDeviceListener;
58 private final Handler mDeviceHandler;
60 private boolean mIdle = true;
62 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
63 new SparseArray<CaptureListenerHolder>();
65 private int mRepeatingRequestId = REQUEST_ID_NONE;
66 private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
67 // Map stream IDs to Surfaces
68 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
70 private final String mCameraId;
72 // Runnables for all state transitions, except error, which needs the
73 // error code argument
75 private final Runnable mCallOnOpened = new Runnable() {
77 if (!CameraDevice.this.isClosed()) {
78 mDeviceListener.onOpened(CameraDevice.this);
83 private final Runnable mCallOnUnconfigured = new Runnable() {
85 if (!CameraDevice.this.isClosed()) {
86 mDeviceListener.onUnconfigured(CameraDevice.this);
91 private final Runnable mCallOnActive = new Runnable() {
93 if (!CameraDevice.this.isClosed()) {
94 mDeviceListener.onActive(CameraDevice.this);
99 private final Runnable mCallOnBusy = new Runnable() {
101 if (!CameraDevice.this.isClosed()) {
102 mDeviceListener.onBusy(CameraDevice.this);
107 private final Runnable mCallOnClosed = new Runnable() {
109 if (!CameraDevice.this.isClosed()) {
110 mDeviceListener.onClosed(CameraDevice.this);
115 private final Runnable mCallOnIdle = new Runnable() {
117 if (!CameraDevice.this.isClosed()) {
118 mDeviceListener.onIdle(CameraDevice.this);
123 private final Runnable mCallOnDisconnected = new Runnable() {
125 if (!CameraDevice.this.isClosed()) {
126 mDeviceListener.onDisconnected(CameraDevice.this);
131 public CameraDevice(String cameraId, StateListener listener, Handler handler) {
132 if (cameraId == null || listener == null || handler == null) {
133 throw new IllegalArgumentException("Null argument given");
135 mCameraId = cameraId;
136 mDeviceListener = listener;
137 mDeviceHandler = handler;
138 TAG = String.format("CameraDevice-%s-JV", mCameraId);
139 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
142 public CameraDeviceCallbacks getCallbacks() {
146 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
147 // TODO: Move from decorator to direct binder-mediated exceptions
148 synchronized(mLock) {
149 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
151 mDeviceHandler.post(mCallOnOpened);
152 mDeviceHandler.post(mCallOnUnconfigured);
157 public String getId() {
162 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
163 // Treat a null input the same an empty list
164 if (outputs == null) {
165 outputs = new ArrayList<Surface>();
167 synchronized (mLock) {
168 checkIfCameraClosed();
170 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
171 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
173 // Determine which streams need to be created, which to be deleted
174 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
175 int streamId = mConfiguredOutputs.keyAt(i);
176 Surface s = mConfiguredOutputs.valueAt(i);
178 if (!outputs.contains(s)) {
179 deleteList.add(streamId);
181 addSet.remove(s); // Don't create a stream previously created
185 mDeviceHandler.post(mCallOnBusy);
191 // TODO: mRemoteDevice.beginConfigure
192 // Delete all streams first (to free up HW resources)
193 for (Integer streamId : deleteList) {
194 mRemoteDevice.deleteStream(streamId);
195 mConfiguredOutputs.delete(streamId);
198 // Add all new streams
199 for (Surface s : addSet) {
200 // TODO: remove width,height,format since we are ignoring
202 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
203 mConfiguredOutputs.put(streamId, s);
206 // TODO: mRemoteDevice.endConfigure
207 } catch (CameraRuntimeException e) {
208 if (e.getReason() == CAMERA_IN_USE) {
209 throw new IllegalStateException("The camera is currently busy." +
210 " You must wait until the previous operation completes.");
214 } catch (RemoteException e) {
219 if (outputs.size() > 0) {
220 mDeviceHandler.post(mCallOnIdle);
222 mDeviceHandler.post(mCallOnUnconfigured);
228 public CaptureRequest.Builder createCaptureRequest(int templateType)
229 throws CameraAccessException {
230 synchronized (mLock) {
231 checkIfCameraClosed();
233 CameraMetadataNative templatedRequest = new CameraMetadataNative();
236 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
237 } catch (CameraRuntimeException e) {
239 } catch (RemoteException e) {
244 CaptureRequest.Builder builder =
245 new CaptureRequest.Builder(templatedRequest);
252 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
253 throws CameraAccessException {
254 return submitCaptureRequest(request, listener, handler, /*streaming*/false);
258 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
259 Handler handler) throws CameraAccessException {
260 if (requests.isEmpty()) {
261 Log.w(TAG, "Capture burst request list is empty, do nothing!");
265 throw new UnsupportedOperationException("Burst capture implemented yet");
269 private int submitCaptureRequest(CaptureRequest request, CaptureListener listener,
270 Handler handler, boolean repeating) throws CameraAccessException {
272 // Need a valid handler, or current thread needs to have a looper, if
274 if (listener != null) {
275 handler = checkHandler(handler);
278 synchronized (mLock) {
279 checkIfCameraClosed();
283 requestId = mRemoteDevice.submitRequest(request, repeating);
284 } catch (CameraRuntimeException e) {
286 } catch (RemoteException e) {
290 if (listener != null) {
291 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
292 handler, repeating));
296 // Queue for deletion after in-flight requests finish
297 if (mRepeatingRequestId != REQUEST_ID_NONE) {
298 mRepeatingRequestIdDeletedList.add(mRepeatingRequestId);
300 mRepeatingRequestId = requestId;
304 mDeviceHandler.post(mCallOnActive);
313 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
314 Handler handler) throws CameraAccessException {
315 return submitCaptureRequest(request, listener, handler, /*streaming*/true);
319 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
320 Handler handler) throws CameraAccessException {
321 if (requests.isEmpty()) {
322 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
326 throw new UnsupportedOperationException("Burst capture implemented yet");
330 public void stopRepeating() throws CameraAccessException {
332 synchronized (mLock) {
333 checkIfCameraClosed();
334 if (mRepeatingRequestId != REQUEST_ID_NONE) {
336 int requestId = mRepeatingRequestId;
337 mRepeatingRequestId = REQUEST_ID_NONE;
339 // Queue for deletion after in-flight requests finish
340 mRepeatingRequestIdDeletedList.add(requestId);
343 mRemoteDevice.cancelRequest(requestId);
344 } catch (CameraRuntimeException e) {
346 } catch (RemoteException e) {
355 public void waitUntilIdle() throws CameraAccessException {
357 synchronized (mLock) {
358 checkIfCameraClosed();
359 if (mRepeatingRequestId != REQUEST_ID_NONE) {
360 throw new IllegalStateException("Active repeating request ongoing");
364 mRemoteDevice.waitUntilIdle();
365 } catch (CameraRuntimeException e) {
367 } catch (RemoteException e) {
372 mRepeatingRequestId = REQUEST_ID_NONE;
373 mRepeatingRequestIdDeletedList.clear();
374 mCaptureListenerMap.clear();
379 public void flush() throws CameraAccessException {
380 synchronized (mLock) {
381 checkIfCameraClosed();
383 mDeviceHandler.post(mCallOnBusy);
385 mRemoteDevice.flush();
386 } catch (CameraRuntimeException e) {
388 } catch (RemoteException e) {
396 public void close() {
397 synchronized (mLock) {
400 if (mRemoteDevice != null) {
401 mRemoteDevice.disconnect();
403 } catch (CameraRuntimeException e) {
404 Log.e(TAG, "Exception while closing: ", e.asChecked());
405 } catch (RemoteException e) {
409 if (mRemoteDevice != null) {
410 mDeviceHandler.post(mCallOnClosed);
413 mRemoteDevice = null;
418 protected void finalize() throws Throwable {
427 static class CaptureListenerHolder {
429 private final boolean mRepeating;
430 private final CaptureListener mListener;
431 private final CaptureRequest mRequest;
432 private final Handler mHandler;
434 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
436 if (listener == null || handler == null) {
437 throw new UnsupportedOperationException(
438 "Must have a valid handler and a valid listener");
440 mRepeating = repeating;
443 mListener = listener;
446 public boolean isRepeating() {
450 public CaptureListener getListener() {
454 public CaptureRequest getRequest() {
458 public Handler getHandler() {
464 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
467 // Constants below need to be kept up-to-date with
468 // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
472 // Error codes for onCameraError
476 * Camera has been disconnected
478 static final int ERROR_CAMERA_DISCONNECTED = 0;
481 * Camera has encountered a device-level error
482 * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
484 static final int ERROR_CAMERA_DEVICE = 1;
487 * Camera has encountered a service-level error
488 * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
490 static final int ERROR_CAMERA_SERVICE = 2;
493 public IBinder asBinder() {
498 public void onCameraError(final int errorCode) {
500 if (isClosed()) return;
502 synchronized(mLock) {
504 case ERROR_CAMERA_DISCONNECTED:
505 r = mCallOnDisconnected;
508 Log.e(TAG, "Unknown error from camera device: " + errorCode);
510 case ERROR_CAMERA_DEVICE:
511 case ERROR_CAMERA_SERVICE:
514 if (!CameraDevice.this.isClosed()) {
515 mDeviceListener.onError(CameraDevice.this, errorCode);
521 CameraDevice.this.mDeviceHandler.post(r);
526 public void onCameraIdle() {
527 if (isClosed()) return;
530 Log.d(TAG, "Camera now idle");
532 synchronized (mLock) {
533 if (!CameraDevice.this.mIdle) {
534 CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
536 CameraDevice.this.mIdle = true;
541 public void onCaptureStarted(int requestId, final long timestamp) {
543 Log.d(TAG, "Capture started for id " + requestId);
545 final CaptureListenerHolder holder;
547 // Get the listener for this frame ID, if there is one
548 synchronized (mLock) {
549 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
552 if (holder == null) {
556 if (isClosed()) return;
558 // Dispatch capture start notice
559 holder.getHandler().post(
562 if (!CameraDevice.this.isClosed()) {
563 holder.getListener().onCaptureStarted(
573 public void onResultReceived(int requestId, CameraMetadataNative result)
574 throws RemoteException {
576 Log.d(TAG, "Received result for id " + requestId);
578 final CaptureListenerHolder holder;
580 synchronized (mLock) {
581 // TODO: move this whole map into this class to make it more testable,
582 // exposing the methods necessary like subscribeToRequest, unsubscribe..
583 // TODO: make class static class
585 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
587 // Clean up listener once we no longer expect to see it.
588 if (holder != null && !holder.isRepeating()) {
589 CameraDevice.this.mCaptureListenerMap.remove(requestId);
592 // TODO: add 'capture sequence completed' callback to the
593 // service, and clean up repeating requests there instead.
595 // If we received a result for a repeating request and have
596 // prior repeating requests queued for deletion, remove those
597 // requests from mCaptureListenerMap.
598 if (holder != null && holder.isRepeating()
599 && mRepeatingRequestIdDeletedList.size() > 0) {
600 Iterator<Integer> iter = mRepeatingRequestIdDeletedList.iterator();
601 while (iter.hasNext()) {
602 int deletedRequestId = iter.next();
603 if (deletedRequestId < requestId) {
604 CameraDevice.this.mCaptureListenerMap.remove(deletedRequestId);
612 // Check if we have a listener for this
613 if (holder == null) {
617 if (isClosed()) return;
619 final CaptureRequest request = holder.getRequest();
620 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
622 holder.getHandler().post(
626 if (!CameraDevice.this.isClosed()){
627 holder.getListener().onCaptureCompleted(
639 * Default handler management. If handler is null, get the current thread's
640 * Looper to create a Handler with. If no looper exists, throw exception.
642 private Handler checkHandler(Handler handler) {
643 if (handler == null) {
644 Looper looper = Looper.myLooper();
645 if (looper == null) {
646 throw new IllegalArgumentException(
647 "No handler given, and current thread has no looper!");
649 handler = new Handler(looper);
654 private void checkIfCameraClosed() {
655 if (mRemoteDevice == null) {
656 throw new IllegalStateException("CameraDevice was already closed");
660 private boolean isClosed() {
661 synchronized(mLock) {
662 return (mRemoteDevice == null);