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.CameraMetadata;
23 import android.hardware.camera2.CameraCharacteristics;
24 import android.hardware.camera2.CaptureRequest;
25 import android.hardware.camera2.CaptureResult;
26 import android.hardware.camera2.ICameraDeviceCallbacks;
27 import android.hardware.camera2.ICameraDeviceUser;
28 import android.hardware.camera2.utils.CameraBinderDecorator;
29 import android.hardware.camera2.utils.CameraRuntimeException;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.util.Log;
35 import android.util.SparseArray;
36 import android.view.Surface;
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Stack;
45 * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
47 public class CameraDevice implements android.hardware.camera2.CameraDevice {
49 private final String TAG;
50 private final boolean DEBUG;
52 // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
53 private ICameraDeviceUser mRemoteDevice;
55 private final Object mLock = new Object();
56 private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
58 private StateListener mDeviceListener;
59 private Handler mDeviceHandler;
61 private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
62 new SparseArray<CaptureListenerHolder>();
64 private final Stack<Integer> mRepeatingRequestIdStack = new Stack<Integer>();
65 // Map stream IDs to Surfaces
66 private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
68 private final String mCameraId;
70 public CameraDevice(String cameraId) {
72 TAG = String.format("CameraDevice-%s-JV", mCameraId);
73 DEBUG = Log.isLoggable(TAG, Log.DEBUG);
76 public CameraDeviceCallbacks getCallbacks() {
80 public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
81 // TODO: Move from decorator to direct binder-mediated exceptions
82 mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
86 public String getId() {
91 public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
92 synchronized (mLock) {
93 HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
94 List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
96 // Determine which streams need to be created, which to be deleted
97 for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
98 int streamId = mConfiguredOutputs.keyAt(i);
99 Surface s = mConfiguredOutputs.valueAt(i);
101 if (!outputs.contains(s)) {
102 deleteList.add(streamId);
104 addSet.remove(s); // Don't create a stream previously created
109 // TODO: mRemoteDevice.beginConfigure
111 // Delete all streams first (to free up HW resources)
112 for (Integer streamId : deleteList) {
113 mRemoteDevice.deleteStream(streamId);
114 mConfiguredOutputs.delete(streamId);
117 // Add all new streams
118 for (Surface s : addSet) {
119 // TODO: remove width,height,format since we are ignoring
121 int streamId = mRemoteDevice.createStream(0, 0, 0, s);
122 mConfiguredOutputs.put(streamId, s);
125 // TODO: mRemoteDevice.endConfigure
126 } catch (CameraRuntimeException e) {
127 if (e.getReason() == CAMERA_IN_USE) {
128 throw new IllegalStateException("The camera is currently busy." +
129 " You must call waitUntilIdle before trying to reconfigure.");
133 } catch (RemoteException e) {
141 public CaptureRequest.Builder createCaptureRequest(int templateType)
142 throws CameraAccessException {
143 synchronized (mLock) {
145 CameraMetadataNative templatedRequest = new CameraMetadataNative();
148 mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
149 } catch (CameraRuntimeException e) {
151 } catch (RemoteException e) {
156 CaptureRequest.Builder builder =
157 new CaptureRequest.Builder(templatedRequest);
164 public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
165 throws CameraAccessException {
166 return submitCaptureRequest(request, listener, handler, /*streaming*/false);
170 public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
171 Handler handler) throws CameraAccessException {
172 if (requests.isEmpty()) {
173 Log.w(TAG, "Capture burst request list is empty, do nothing!");
177 throw new UnsupportedOperationException("Burst capture implemented yet");
181 private int submitCaptureRequest(CaptureRequest request, CaptureListener listener,
182 Handler handler, boolean repeating) throws CameraAccessException {
184 // Need a valid handler, or current thread needs to have a looper, if
186 if (handler == null && listener != null) {
187 Looper looper = Looper.myLooper();
188 if (looper == null) {
189 throw new IllegalArgumentException(
190 "No handler given, and current thread has no looper!");
192 handler = new Handler(looper);
195 synchronized (mLock) {
200 requestId = mRemoteDevice.submitRequest(request, repeating);
201 } catch (CameraRuntimeException e) {
203 } catch (RemoteException e) {
207 if (listener != null) {
208 mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request,
209 handler, repeating));
213 mRepeatingRequestIdStack.add(requestId);
221 public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
222 Handler handler) throws CameraAccessException {
223 return submitCaptureRequest(request, listener, handler, /*streaming*/true);
227 public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
228 Handler handler) throws CameraAccessException {
229 if (requests.isEmpty()) {
230 Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
234 throw new UnsupportedOperationException("Burst capture implemented yet");
238 public void stopRepeating() throws CameraAccessException {
240 synchronized (mLock) {
242 while (!mRepeatingRequestIdStack.isEmpty()) {
243 int requestId = mRepeatingRequestIdStack.pop();
246 mRemoteDevice.cancelRequest(requestId);
247 } catch (CameraRuntimeException e) {
249 } catch (RemoteException e) {
258 public void waitUntilIdle() throws CameraAccessException {
260 synchronized (mLock) {
261 checkIfCameraClosed();
262 if (!mRepeatingRequestIdStack.isEmpty()) {
263 throw new IllegalStateException("Active repeating request ongoing");
267 mRemoteDevice.waitUntilIdle();
268 } catch (CameraRuntimeException e) {
270 } catch (RemoteException e) {
278 public void setDeviceListener(StateListener listener, Handler handler) {
279 synchronized (mLock) {
280 mDeviceListener = listener;
281 mDeviceHandler = handler;
286 public void flush() throws CameraAccessException {
287 synchronized (mLock) {
289 mRemoteDevice.flush();
290 } catch (CameraRuntimeException e) {
292 } catch (RemoteException e) {
300 public void close() {
302 // TODO: every method should throw IllegalStateException after close has been called
304 synchronized (mLock) {
307 if (mRemoteDevice != null) {
308 mRemoteDevice.disconnect();
310 } catch (CameraRuntimeException e) {
311 Log.e(TAG, "Exception while closing: ", e.asChecked());
312 } catch (RemoteException e) {
316 mRemoteDevice = null;
322 protected void finalize() throws Throwable {
331 static class CaptureListenerHolder {
333 private final boolean mRepeating;
334 private final CaptureListener mListener;
335 private final CaptureRequest mRequest;
336 private final Handler mHandler;
338 CaptureListenerHolder(CaptureListener listener, CaptureRequest request, Handler handler,
340 if (listener == null || handler == null) {
341 throw new UnsupportedOperationException(
342 "Must have a valid handler and a valid listener");
344 mRepeating = repeating;
347 mListener = listener;
350 public boolean isRepeating() {
354 public CaptureListener getListener() {
358 public CaptureRequest getRequest() {
362 public Handler getHandler() {
369 public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
372 public IBinder asBinder() {
376 // TODO: consider rename to onMessageReceived
378 public void notifyCallback(int msgType, int ext1, int ext2) throws RemoteException {
380 Log.d(TAG, "Got message " + msgType + " ext1: " + ext1 + " , ext2: " + ext2);
382 // TODO implement rest
386 public void onResultReceived(int requestId, CameraMetadataNative result)
387 throws RemoteException {
389 Log.d(TAG, "Received result for id " + requestId);
391 final CaptureListenerHolder holder;
393 synchronized (mLock) {
394 // TODO: move this whole map into this class to make it more testable,
395 // exposing the methods necessary like subscribeToRequest, unsubscribe..
396 // TODO: make class static class
398 holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
400 // Clean up listener once we no longer expect to see it.
402 // TODO: how to handle repeating listeners?
403 // we probably want cancelRequest to return # of times it already enqueued and
405 if (holder != null && !holder.isRepeating()) {
406 CameraDevice.this.mCaptureListenerMap.remove(requestId);
410 // Check if we have a listener for this
411 if (holder == null) {
415 final CaptureRequest request = holder.getRequest();
416 final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId);
418 holder.getHandler().post(
422 holder.getListener().onCaptureCompleted(
432 private void checkIfCameraClosed() {
433 if (mRemoteDevice == null) {
434 throw new IllegalStateException("CameraDevice was already closed");